Compare commits

...

227 Commits

Author SHA1 Message Date
Justin Clark-Casey (justincc)
e717398f6c Add experimental "slow frames" stat, available in "show stats" and via the monitoring module.
This increments a SlowFrames counter if a frame takes over 120% of maximum time.
This commit also introduces a generic OpenSim.Framework.Monitoring.Stat which is available to any code that wants to register a statistic.
This is more granualar than asking objects to create their own reports.
At some point this will supersede earlier IMonitor and IAlert facilities in MonitoringModule which are only available to scene code.
2012-10-04 00:32:42 +01:00
teravus
a2ab3b88de Soliciting for comments on smoothness of physics objects for this build.
This introduces expected contact point hints to the ODE Collider to better determine when to throttle updates as excessive.   This /should/ smooth physics objects out again, however, I cannot know every configuration of OpenSimulator, so I'm requesting that testers please examine this change on their build.
Thanks!
2012-10-03 18:30:44 -04:00
Mic Bowman
b9baab5521 Fix the use of the wrong index when locating the assets associated
with wearables. The fact that this hasn't caused problems earlier
suggests either that no one is using multiple layers of wearables or
that this code is useless because the assets are coming in with the
wearables request.
2012-10-03 10:45:57 -07:00
Melanie
653a98e891 Fix a viewer crash issue
Conflicts:

	OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
2012-10-03 17:39:13 +01:00
teravus
e56ef2720e I propose that 0.5m/step change for linear velocity is too big of a change to control the reporting of a new angular velocity. I think that this could be here for one of two reasons, 1. vehicles and llMoveToTarget with axis lock, or 2. To attempt to make things look more stable in the physics scene then they really are, however, this also really affects the angular velocity reporting negatively causing things to spin wildly and jump back into place repeatedly. To compromise, if the prim is a vehicle or is being used as a motor target, the original functionality is still applied. If that's not the case, angular velocity is reported with a linear velocity of 0.02m/step. To be clear on the effect of the physical world... When you push things, there's still a lag time where you walk into the object but once the object is in motion, it begins to move as you would expect so results in slightly more realistic motion. 2012-10-03 02:30:23 -04:00
teravus
df3d1d1301 Minor Modification, switch /2 to 0.5f in ODEPrim.Velocity 2012-10-02 23:14:35 -04:00
Robert Adams
a5b8359b8e BulletSim: fix crash on load that showed up on 32bit Linux. 2012-10-02 17:04:33 -07:00
Melanie
ca5c0814f4 Merge branch 'master' of melanie@opensimulator.org:/var/git/opensim 2012-10-02 23:02:53 +01:00
Melanie
f1886c449c Attempt to fix Mantis #6311. Honor a destination folder if one is given 2012-10-02 23:02:01 +01:00
SignpostMarv
a1679517d3 correcting typo 2012-10-02 22:14:47 +01:00
SignpostMarv
f555febdb7 taking into account the recent introduction of implicit operators 2012-10-02 22:14:46 +01:00
SignpostMarv
ce7694c108 added perms checking, duplicated functionality to methods that do not require perms and have higher threat level 2012-10-02 22:14:46 +01:00
SignpostMarv
c677c04f10 fixing copypasta 2012-10-02 22:14:46 +01:00
SignpostMarv
35b7c80e0b implementing osDropAttachment & osDropAttachmentAt 2012-10-02 22:14:46 +01:00
SignpostMarv
e81e19a3b4 string format arguments in wrong order 2012-10-02 22:11:40 +01:00
Robert Adams
210f227fe6 BulletSim: Make parameter value defaults match what should be the default and what is in OpenSimDefaults.ini. Comment and debug printout changes. 2012-10-02 11:14:15 -07:00
Robert Adams
ce47d0c454 BulletSim: Add ForcePosition and ForceOrientation to BSPhysObject and to its children of BSPrim and BSCharacter. 2012-10-02 11:14:09 -07:00
Robert Adams
d8a786870b BulletSim: Fix problem where box shapes were not being rebuilt if the shape type changed. 2012-10-02 11:14:03 -07:00
Robert Adams
ce97ebdc88 BulletSim: Fix linkset problem where delayed manipulations of child objects was using the child shape address at call time rather than the one created at taint time. 2012-10-02 11:13:57 -07:00
Robert Adams
b6b505163c BulletSim: call ForcePosition and ForceOrientation in BSDynamics so there is no lag between what the vehicle code sees and what the physics engine is using. 2012-10-02 11:13:51 -07:00
Robert Adams
1e5869dcf6 Add Flush() method to LogWriter. Also correct line endings to Linux form. 2012-10-02 11:13:45 -07:00
Robert Adams
f2c78281ce BulletSim: fix the FloatOnWater code so avatars can normally go underwater. 2012-10-02 11:13:39 -07:00
Robert Adams
33617e09a1 BulletSim: impliment FloatOnWater OS function. 2012-10-02 11:13:32 -07:00
Robert Adams
c1740a2903 Correct my name in CONTRIBUTORS.txt 2012-10-02 11:13:26 -07:00
Robert Adams
5221f2421e BulletSim: remove warnings for unused variables. 2012-10-02 11:13:20 -07:00
Melanie
54ec8b197b Add money event routing to the script engine. 2012-10-02 01:25:55 +01:00
Melanie
14b659e550 Send money() events to the clicked prim. Reverting a change I made over a year ago. 2012-10-02 01:17:46 +01:00
Melanie
402128bb53 Prevent the ExtraSettings code from crashing SQLite driven sims. 2012-10-02 00:51:38 +01:00
Diva Canto
0b9bf236dd On more virtual method 2012-10-01 15:55:24 -07:00
Melanie
503ce70f74 Allow setting connection limits, part 2 2012-09-30 16:31:23 +01:00
Melanie
060d6fe8f4 Allow setting max connections for an endpoint 2012-09-30 16:30:16 +01:00
Melanie
7cc89cdadc Add using clause I missed 2012-09-30 15:57:49 +01:00
Melanie
f8a4b61954 Allow up to 12 simultaneous connections to a given endpoint rather than 2 2012-09-30 15:49:51 +01:00
Diva Canto
91a5c602e3 Revert "Added request.Proxy=null everywhere, as discussed in http://stackoverflow.com/questions/2519655/httpwebrequest-is-extremely-slow."
But the patch is here, in case anyone wants to try it.

This reverts commit 531edd51d8.
2012-09-30 07:48:03 -07:00
Diva Canto
531edd51d8 Added request.Proxy=null everywhere, as discussed in http://stackoverflow.com/questions/2519655/httpwebrequest-is-extremely-slow.
Thanks R.Gunther (rigun@rigutech.nl) https://lists.berlios.de/pipermail/opensim-users/2012-September/010986.html
2012-09-30 07:22:55 -07:00
Diva Canto
14a4b146bf Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-30 06:52:19 -07:00
Diva Canto
5b69872655 Made a method of GridUserService virtual so it can be overridden. 2012-09-30 06:51:49 -07:00
Justin Clark-Casey (justincc)
4e9509dafa Get rid of some unnecessary casts in RemoteAdminPlugin.
This was stopping http://code.google.com/p/opensimtools/wiki/RemoteAdminPHPClass from working with some methods (e.g. create_user).
However, this casting has been around for at least 2 years so I'm puzzled that it worked in the first place, though it probably is extremely old sample code.
2012-09-29 02:57:28 +01:00
Justin Clark-Casey (justincc)
f457952755 Fix bug where debug http level 6 could not be specified. Also converts newlines at this level to '\n' to enable them to be logged. 2012-09-29 02:38:21 +01:00
Justin Clark-Casey (justincc)
818379ba14 Fire EventManager.TriggerOnAttach (and hence LSL attach event) when an object is attached from the scene.
Enables previously disabled regression test.
Based on SingpostMarv's patch in http://opensimulator.org/mantis/view.php?id=6302 but I prefer a simpler approach that does not expose a resume scripts option right now.
2012-09-29 01:14:23 +01:00
Justin Clark-Casey (justincc)
fae869c4ca Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-29 00:09:12 +01:00
Justin Clark-Casey (justincc)
d588467d5b Extend attachment regression test to check for appropriate attach event firing.
This reveals that the event is not being fired when an object is attached from ground, which is incorrect.
This check is temporairly disabled.
2012-09-29 00:07:11 +01:00
nebadon
e3d6d36c72 testing for xml-rpc errors in git repo. 2012-09-28 13:00:08 -07:00
Robert Adams
7f96bb601a BulletSim: update SOs and DLLs 2012-09-28 12:51:47 -07:00
Robert Adams
76e9cc41bd BulletSim: remember to release the physical body and shape when a prim is destroyed. This fixes many problems with physical linksets. 2012-09-28 12:34:50 -07:00
Robert Adams
6f89975526 BulletSim: add separate runtime and taint-time linkset children lists to keep the creation of constraints separate from runtime. 2012-09-27 22:02:13 -07:00
Robert Adams
74dea4cfd5 BulletSim: rename some constraint variables to be consistant with other name use.
Added callbacks for shape and body changes in GetBodyAndShape() so the linkset
    constraints can be picked up and restored. A better design might be to have
    a "prim shape changed" event. Think about that.
Added constraint types to general constraint class.
2012-09-27 22:02:08 -07:00
Robert Adams
7b65985047 BulletSim: remove the trailing spaces from lines to make git happier 2012-09-27 22:02:03 -07:00
Robert Adams
5781d4d52d BulletSim: remove the unused body management code from BSPrim. There is no going back now. 2012-09-27 22:01:57 -07:00
Robert Adams
f82b903dee BulletSim: Fix linkset crash. Caused by the different body and shape
pointers at runtime and at taint-time. Now passes the body into the
    taint.
Vehicles zero inertia when active to eliminate Bullet's contribution
    to vehicle motion.
2012-09-27 22:01:52 -07:00
Robert Adams
4589bc84a3 BulletSim: Terrain sets proper collision flags on creation.
Static objects are set to ISLAND_SLEEPING rather than DISABLE_SIMULATION.
    Might reconsider this and, alternatively, have dynamic objects force activation.
Clean up use of DetailLog().
2012-09-27 22:01:47 -07:00
Robert Adams
735d89e369 BulletSim: btGhostObjects working to make 'volume detect' work.
Rearrangement and cleanup of shape collection code. Much more readable.
Enabling and use of collision filters and masks.
Addition of ID to body creation BulletSimAPI calls so always set in
    shape for collision reporting.
Change default of ShouldSplitSimulationIslands and ShouldRandomizeSolverOrder
    from 'false' to 'true'. When 'false', this suppresses NO_CONTACT_RESPONSE
    which makes volume detect fail.
2012-09-27 22:01:42 -07:00
Robert Adams
d016051fa0 BulletSim: renamed members of BulletShape, BulletSim and BulletBody
so the members case is consistant. Caused modifications everywhere.
New logic in BSShapeCollection to track use and sharing of shapes.
    I just reslized, though, that shapes cannot be shared because the
    shape's UserPointer is the localID of the prim and is required
    for tracking collisions. More changes coming.
Added DuplicateCollisionShape2() to API and changed BuildNativeShape2
    to take a ShapeData structure so don't have to pass so many parameters.
    This matches the latest version of BulletSim.dll.
Additions and removal of DetailLog() statements for debugging.
2012-09-27 22:01:37 -07:00
Robert Adams
42802669dd BulletSim: fix regression that caused cylindar shapes to have a box collision shape 2012-09-27 22:01:31 -07:00
Robert Adams
22290ef35a BulletSim: complete code for managed code shape and body tracking. Not debugged.
Eliminate some null exceptions created adding the above code.
Add and remove some detailed logging statements.
2012-09-27 22:01:26 -07:00
Robert Adams
a27e4ce6cb BulletSim: add class and infrastructure for shape and object
tracking in the C# code. Needed for the changing body type
    (to and from GhostObjects) for volumeDetect.
2012-09-27 22:01:21 -07:00
Robert Adams
91efccabdc BulletSim: Convert BSCharacter to use common BSPhysObject code and variables.
Fix avatar height calculation to properly account for the capsule ends.
Rearrange some locking in TerrainManager to eliminate possible race conditions.
Move DetailLog() definition into common BSPhysObject class.
Some variable renaming to make usage clearer (refactor.rename makes this so easy).
2012-09-27 22:01:16 -07:00
Robert Adams
ee7cda261c BulletSim: move a bunch of common logic out of BSPrim and BSCharacter
and into the parent class BSPhysObject.
Rework collision logic to enable extra collision after done colliding.
Rename 'Scene' to 'PhysicsScene' to differentiate it from the simulator 'Scene'.
2012-09-27 22:01:11 -07:00
Justin Clark-Casey (justincc)
d26fbf727a Comment out unused RestPlugins text in OpenSimDefaults.ini 2012-09-28 02:19:15 +01:00
Justin Clark-Casey (justincc)
b5af17c8e3 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-28 01:54:39 +01:00
Justin Clark-Casey (justincc)
3db10fdbef Lock GDI+ portion og VectorRenderModule.GetDrawStringSize() to prevent concurrent thread use provoking mono crashes.
Same rationale as commit 13690582.
2012-09-28 01:50:21 +01:00
Diva Canto
6511c3b5c6 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-27 16:43:38 -07:00
Diva Canto
7a50705188 Removed the bits about the TOSModule. That module doesn't go into core. WARNING: migration on GridUser withdrawn too, but left the migration number there. 2012-09-27 16:43:18 -07:00
nebadon
0e5d3d051b another osgitbot test from github.. mistakes were made! 2012-09-27 06:51:14 -07:00
nebadon
f5fac2c916 another test to see if gitbot is alive yet 2012-09-27 06:37:43 -07:00
nebadon
581e76942f just testing to see if pcampbot is alive yet 2012-09-26 17:49:48 -07:00
Justin Clark-Casey (justincc)
2bf42f30af Add MaxPrimsUndo config setting to [Startup] section of OpenSim.ini.
This controls how many undo steps the simulator will store for each prim.
Default is now 20 rather than 5 as it briefly was.
The default number could be increased through this is a memory tradeoff which will scale with the number of prims in the sim and level of activity.
2012-09-27 00:12:34 +01:00
Justin Clark-Casey (justincc)
b9934fc4db Don't store undo states if a scene object is manipulated when it is not in a scene.
Adds regression test for this.
2012-09-26 23:31:10 +01:00
Justin Clark-Casey (justincc)
31c636f1e4 refactor: Change control structures in SOP.StoreUndoState() to reduce nesting. 2012-09-26 23:25:50 +01:00
Justin Clark-Casey (justincc)
36e3123069 Make it possible to rescale SOGs when they are not in a scene. 2012-09-26 23:17:21 +01:00
Justin Clark-Casey (justincc)
327320d1a7 Enforce existing 5 action hardcoded undo limit.
This was present in the code but not enforced, which led to a memory leak over time as part properties were changed, whether by viewer, script or another source.
This commit enforces that limit, which will soon become configurable.
Regression test for undo limit added
Should help with http://opensimulator.org/mantis/view.php?id=6279
2012-09-26 22:49:44 +01:00
Justin Clark-Casey (justincc)
21f30a1b5f Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-26 22:49:05 +01:00
Justin Clark-Casey (justincc)
90dd5844d6 Add basic undo/redo regression tests. 2012-09-26 21:28:43 +01:00
Diva Canto
3c77b8f463 Use GridUser properly for foreign users. 2012-09-26 12:40:41 -07:00
Diva Canto
80133f9ebe Show the user's home url too on the console command. 2012-09-26 11:27:31 -07:00
Diva Canto
2f61763923 Not really a change, just added a reminder to fix something here. 2012-09-26 11:26:51 -07:00
Diva Canto
7d2cd0d935 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-25 20:04:10 -07:00
Diva Canto
ae58cf4224 TOS module. WARNING: migration in GridUser table. 2012-09-25 20:03:49 -07:00
Diva Canto
495a3e7daf Oops, that should be inside the curly bracket. 2012-09-25 19:54:42 -07:00
Justin Clark-Casey (justincc)
92afa6aa44 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-25 23:32:52 +01:00
Justin Clark-Casey (justincc)
ddd9384b39 Fix very recently introduced race condition where a CreateNewItem outracing an UploadAsset request could throw an exception because m_asset did not yet exist.
This was accidentally introduced in 4fc0cfb
This commit also consistently removes the AssetXferUploader when the transaction completes, no matter if it completed on asset upload or item operation.
The amount of data being retained was small, since this was clothing/bodypart metadata in the asset rather than textures themselves.
2012-09-25 23:30:25 +01:00
BlueWall
4dc27aa91f Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-25 18:14:06 -04:00
Justin Clark-Casey (justincc)
b96a53962b Comment out old m_storeLocal from AssetXferUploader.
This was only used if none of new item, update item or update task item had been set.
But since all transactions go through these paths this old code is redundant.
2012-09-25 23:13:45 +01:00
BlueWall
3c2fb77133 Format OpenSim.ini.example
Make lines fint in 80x24 terminal for easier reading
2012-09-25 18:13:32 -04:00
Justin Clark-Casey (justincc)
eb5bec96e4 Insert transaction ID into AssetXferUploader constructor rather than at UploadAsset() to prevent item creation failure when NewInventoryItem thread reachs the object first.
This was preventing the previous race condition fix in 4fc0cfb from actually working.
This commit also removes some of the pointless transaction id checks - these conditions are already being enforced in AgentAssetsTransactions.
2012-09-25 22:54:20 +01:00
Justin Clark-Casey (justincc)
2f795e4fa6 Move UDP update task item code to AssetXferUploader to match existing create user item and update user item mechanisms
This is done for consistency and to allow removal or some access methods that increase code complexity.
However, this path has not been used for a long time, not even by LL 1.23 - viewers use caps http upload for this instead
2012-09-25 22:08:11 +01:00
Justin Clark-Casey (justincc)
4fc0cfba3c Fix occasional race condition failure when creating new clothing/body parts in the viewer or updating existing assets.
On creating these items, the viewer sends a UDP AssetUploadRequest followed by a CreateInventoryItem.
It was possible for the CreateInventoryItem/UpdateInventoryItem to occasionally outrace the AssetUploadRequest and fail to find an initialized Xfer object, at which point the item create would fail.
So instead we always set up a Xfer object on either the asset or inventory item update request.
This does not introduce a new race because code already exists to delay the item operation until the asset is uploaded if necessary (but this only worked if the xfer object already existed)
2012-09-25 21:35:39 +01:00
Diva Canto
77355295da Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-25 10:46:42 -07:00
Diva Canto
4ec2fc4cc2 Deleted extraneous debug message 2012-09-25 10:46:21 -07:00
SignpostMarv
020103c51e Documenting object-related events 2012-09-24 22:59:02 +01:00
Justin Clark-Casey (justincc)
ac02cadbdb Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-24 22:52:33 +01:00
SignpostMarv
1cfd63d0b4 Documenting LSL script-related events 2012-09-24 22:44:37 +01:00
Diva Canto
1c42337381 HG Rez object: warn the user if the item or asset cannot be found. 2012-09-22 14:41:19 -07:00
Diva Canto
87d810217e Guard against inventory get failures. 2012-09-22 14:01:51 -07:00
Diva Canto
772aedc731 Make BaseHttpServer throws say something useful. 2012-09-22 14:01:07 -07:00
Diva Canto
fb6d6e5cca HG 2.0: User Agent Service now can also control where the local users can go. Domain-name and user-level based. \o/ 2012-09-22 11:11:48 -07:00
Diva Canto
48f4b32d7f More HG 2.0: access control at the Gatekeeper. \o/ 2012-09-21 21:03:14 -07:00
Diva Canto
ebd99c9e3e Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-21 17:59:42 -07:00
Diva Canto
21a6ef5bb6 This fixes HG attachments' missing assets (textures, etc).
Also, further improvements on HGUuidGatherer: if the assets are already in this grid don't fetch them again.
2012-09-21 17:58:44 -07:00
Michelle Argus
a6f7328216 Remove deprecated parameters for region_id.
Remove deprecated parameters for region_id as anounced in january 2012.
2012-09-22 01:30:38 +01:00
Justin Clark-Casey (justincc)
b36ef60aca Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-22 01:05:33 +01:00
SignpostMarv
8d7b1f8aa0 Documenting non-LSL script-related events 2012-09-22 00:57:29 +01:00
Diva Canto
b0da4b8d13 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-21 16:45:35 -07:00
Diva Canto
cfa022700d Moved the small HGUuidGatherer class to the file where its parent class is. No need to keep 2 separate files. 2012-09-21 16:45:16 -07:00
Justin Clark-Casey (justincc)
9ab5c5040d Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-22 00:26:32 +01:00
Justin Clark-Casey (justincc)
5df3944607 Fix llListFindList() returning no match when there is a match with a script constant component in the source list.
Adds regression test for this case.
Based on http://opensimulator.org/mantis/view.php?id=6156
Thanks SignpostMarv.
2012-09-22 00:23:25 +01:00
Diva Canto
fb2ace6fff Removed redundant asset fetches on HGAssetMapper. The UuidGatherer already downloads the assets, so we don't need to do it again... 2012-09-21 15:55:58 -07:00
Diva Canto
f931c0a868 Minor: may avoid crashes of sims that still don't have this configuration section. 2012-09-21 15:04:41 -07:00
Diva Canto
f4cc19968c Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-21 07:45:26 -07:00
Diva Canto
b542622b3a HG 2.0: added asset import/export policies at the sim too. 2012-09-21 07:44:18 -07:00
Diva Canto
5f97b3e1d9 Minor: change the return value of unsuccessful posts to string.Empty. 2012-09-21 06:41:32 -07:00
Diva Canto
e379566e6e Improvement over last commit: refactor the asset permissions code, so that it can be used by both the HG Asset Service and the simulator. Also renamed the config vars to something more intuitive 2012-09-20 19:50:57 -07:00
Justin Clark-Casey (justincc)
80f486c778 minor: Make slow outgoing request log messages consistent with other log messages 2012-09-21 01:59:28 +01:00
Justin Clark-Casey (justincc)
632a42e2b1 Rename UuidGather.m_assetCache to m_assetService. If HGUuidGatherer hasn't been instantiated with an assetServerURL then call down to overriden UuidGatherer.GetAsset() instead of calling m_assetService.GetAsset() itself - these two codepaths are now identical. 2012-09-21 01:57:13 +01:00
Justin Clark-Casey (justincc)
bceef401fa Simplify UuidGatherer by performing asset fetch synchronously rather than using the async call but waiting for completion anyway! 2012-09-21 01:36:23 +01:00
Justin Clark-Casey (justincc)
1b0abf8f0c Comment out the long unused afaik HTTP agent handlers.
As far as I know, this was only used by the IBM Rest modules, much of which has been commented out for a very long time now.  Other similar code uses HTTP or stream handlers instead.
So commenting this out to reduce code complexity and the need to make this facility consistent with the others where it may not be used anyway.
If this facility is actually being used then please notify me or uncomment it if you are core.
2012-09-21 00:29:13 +01:00
Justin Clark-Casey (justincc)
e29d563557 Add request number counting to incoming HTTP requests in the same way that this was already being done for outgoing HTTP requests.
This allows us to associate debug logging messages with the right request.
It also allows us to put a request number on 'long request' logging even if other debug logging is not enabled, which gives us some idea of whether every request is suffering this problem or only some.
This is a separate internal number not associated with any incoming number in the opensim-request-id header, this will be clarified when logging of this incoming request number is re-enabled.
This commit also adds port number to HTTP IN logging to allow us to distinguish between different request numbers on different ports.
2012-09-21 00:09:17 +01:00
Diva Canto
33aa6d172f Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-20 15:51:27 -07:00
Diva Canto
3089b6d824 More HG2.0: Added permission policies in HGAsset Service based on asset types. The policies are given in the config. This is only half of the story. The other half, pertaining to exports/imports made by the sim, will be done next. 2012-09-20 15:49:22 -07:00
Justin Clark-Casey (justincc)
a5b3989e5d Insert a new log level 4 for HTTP IN and HTTP OUT that will log how long the request took.
This is only printed if debug http level >= 4 and the request didn't take more than the time considered 'long', in which case the existing log message is printed.
This displaces the previous log levels 4 and 5 which are now 5 and 6 respectively.
2012-09-20 23:18:19 +01:00
Justin Clark-Casey (justincc)
387a1bb283 Add ability to turn on/off logging of outgoing HTTP requests flowing through WebUtil.
This is for debugging purposes.
This is controlled via the "debug http" command which can already log incoming requests.
This now gains a mandatory parameter of in, out or all to control what is logged.
Log messages are also shortened and labelled and HTTP IN or HTTP OUT to be consistent with existing UDP PACKET IN and PACKET OUT messages.
2012-09-20 23:17:44 +01:00
Justin Clark-Casey (justincc)
b481a78234 Don't fail to create an IRC nick if nick randomization is disabled in the IRC module.
Patch from http://opensimulator.org/mantis/view.php?id=6293
Thanks Starflower.
2012-09-20 02:01:01 +01:00
SignpostMarv
d667f9d260 Documentation of teleport-related events 2012-09-20 01:56:18 +01:00
SignpostMarv
696b3f66c4 minor tweaks to existing comments for IDE goodness 2012-09-20 01:50:40 +01:00
SignpostMarv
49e2872f9e Documentation of agent-related events 2012-09-20 01:50:40 +01:00
SignpostMarv
61f4523e01 Documentation of object-related events 2012-09-20 01:48:50 +01:00
SignpostMarv
a74167bb06 Documentation of OnPluginConsole 2012-09-20 01:47:03 +01:00
Justin Clark-Casey (justincc)
f2a9d26118 Make ResendAppearanceUpdates = true by default in [Appearance] in OpenSimDefaults.ini.
This resends appearance uuids to avatars in the scene once a minute.
I have seen this help in the past resolve grey appearance problems where viewers have for unknown reasons sometimes ignored the packet.
The overhead is very small since only the UUIDs are sent - the viewer then requests the texture only if it does not have it cached.
This setting will not help with cloudy avatars which are usually due to the viewer not uploading baked texture data or uploading something that isn't valid JPEG2000
2012-09-20 01:40:05 +01:00
Justin Clark-Casey (justincc)
e50b6b5334 Update libopenjpeg libraries used by libopenmetaverse back up to 1.5, this time using lkalif's linux libraries built against a much earlier libc (2.7) 2012-09-20 01:26:35 +01:00
Justin Clark-Casey (justincc)
967d42d393 Correctly override and call base OpenSimTestCase.SetUp() method in GridConnectorsTests and ArchiverTests.
Remove unrelated compile warning from AttachmentsModuleTests.
2012-09-19 01:06:42 +01:00
Justin Clark-Casey (justincc)
e50155ebca Downgrade libopenjpeg back to 1.3 from 1.5.
This is because libopenjpeg 1.5 appears to require a minimum of glibc 2.14, whereas at least one fairly recent distro (openSUSE 11.4 from 2011-03-10) only has glibc
Further investigation pending.
2012-09-19 00:35:06 +01:00
Justin Clark-Casey (justincc)
f99278c956 Add openmetaverse_data from libopenmetaverse to allow testing of texture baking via bot rather than just throwing out errors 2012-09-18 23:57:00 +01:00
justincc
95809cabd9 Update libopenmetaverse components to commit f5cecaa
Among other things this allows pCampbot to work under Windows since libopenmateverse now ships the same log4net.dll (publicly signed) as OpenSimulator
This also updates the libopenmetaverse embedded libopenjpeg from 1.3 to 1.5.
DLL naming and mapping for non-Windows libopenjpeg changes to remove version number to make future udpates easier and bring it into line with names of other shipped DLLs.
libopenjpeg updates have been made for OSX, Windows (32 and 64 bit) and Linux (32 and 64 bit).  Please report any issues.
2012-09-18 23:45:06 +01:00
BlueWall
9d973ec3b3 Cleanup from prev. commit
Make correct defaults to Phys/nonPhys prims to fix errors in prev. commit
2012-09-18 10:12:32 -04:00
BlueWall
d29fc53052 Fix some inconsistencies in configurartion: NonPhys prims
Fix inconsistencies between configuration parameter names and their description names. Changing the configuration parameters for non physical prim size min-max from Nonphys* to NonPhys*.

  Please update your OpenSim.ini and Regions.ini to reflect these changes.
2012-09-18 09:53:45 -04:00
Mic Bowman
91312daeb2 Moving ScriptModuleComms into the CoreModules tree. 2012-09-17 09:04:54 -07:00
SignpostMarv
0e5f5538bc Documentation of IScriptModuleComms.RegisterConstant and IScriptModuleComms.LookupModConstant
Signed-off-by: Melanie <melanie@t-data.com>
2012-09-17 14:15:52 +01:00
SignpostMarv
f9721573d9 Implementing ability to register script constants and invocations on a region module automatically 2012-09-17 14:15:47 +01:00
SignpostMarv
c7948a669a preventing a null reference exception from being thrown
Signed-off-by: Melanie <melanie@t-data.com>
2012-09-17 13:29:15 +01:00
SignpostMarv
5e626ce55d script invocations cannot have void return type 2012-09-17 13:29:01 +01:00
Diva Canto
380962d359 HG2.0: Generalized the allowed HG appearances to be more than 1. 2012-09-16 16:45:57 -07:00
Diva Canto
daa4745fb7 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-15 19:35:33 -07:00
Diva Canto
de69a24574 More on HG2.0: added the possibility of controlling the appearance that avies use to visit other grids. Not as good as I wanted, but good enough. Unfortunately we can't switch the appearance from under the avie without getting into a lot of weirdnesses because appearance is viewer-controlled. So instead, when this control is on, I'm disallowing HG-TP unless the user is wearing an allowed HG appearance -- the user gets a warning and needs to switch appearance. WARNING: I'm still not committing the config vars because this is still not ready for ppl to test. 2012-09-15 19:33:51 -07:00
Robert Adams
b602b476ad BulletSim: update DLLs and SOs and remove some debugging code. 2012-09-15 15:36:13 -07:00
Robert Adams
c77be802d2 BulletSim: some debugging prints in BSPrim for tracking changes in linkset children. 2012-09-15 15:32:16 -07:00
Robert Adams
1826b2b18e BulletSim: add the debugging routine DumpRigidBody2() to API2. 2012-09-15 15:32:11 -07:00
Robert Adams
f35bd6eb7d BulletSim: another attempt at computing physics FPS correctly. 2012-09-15 15:32:05 -07:00
Robert Adams
dcb94b8a24 BulletSim: remove timeStep parameter from calls for vehicle parameter setting. There is no reason these should be using the simulation time interval for parameter calculation. 2012-09-15 15:31:59 -07:00
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
208 changed files with 21149 additions and 3732 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

@@ -1,5 +1,5 @@
<<<>>>>The following people have contributed to OpenSim (Thank you
for your effort!)
for your effort!)
= Current OpenSim Developers (in very rough order of appearance) =
These folks represent the current core team for OpenSim, and are the
@@ -16,7 +16,7 @@ people that make the day to day of OpenSim happen.
* BlueWall (James Hughes)
* Nebadon Izumi (Michael Cerquoni, OSgrid)
* Snoopy Pfeffer
* Richard Adams (Intel)
* Robert Adams (Intel)
= Core Developers Following the White Rabbit =
Core developers who have temporarily (we hope) gone chasing the white rabbit.
@@ -209,3 +209,4 @@ In addition, we would like to thank:
* The NANT Developers
* Microsoft (.NET, MSSQL-Adapters)
*x

View File

@@ -572,7 +572,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
region.ExternalHostName = (string) requestData["external_address"];
bool persist = Convert.ToBoolean((string) requestData["persist"]);
bool persist = Convert.ToBoolean(requestData["persist"]);
if (persist)
{
// default place for region configuration files is in the
@@ -728,7 +728,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
responseData["success"] = true;
responseData["region_name"] = region.RegionName;
responseData["region_id"] = region.RegionID.ToString();
responseData["region_uuid"] = region.RegionID.ToString(); //Deprecate July 2012
m_log.Info("[RADMIN]: CreateRegion: request complete");
}
@@ -982,8 +981,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
string lastName = (string) requestData["user_lastname"];
string password = (string) requestData["user_password"];
uint regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
uint regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
uint regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
uint regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
string email = ""; // empty string for email
if (requestData.Contains("user_email"))
@@ -1180,9 +1179,9 @@ namespace OpenSim.ApplicationPlugins.RemoteController
if (requestData.ContainsKey("user_password")) password = (string) requestData["user_password"];
if (requestData.ContainsKey("start_region_x"))
regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
if (requestData.ContainsKey("start_region_y"))
regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
// if (requestData.ContainsKey("start_lookat_x"))
// ulaX = Convert.ToUInt32((Int32) requestData["start_lookat_x"]);
@@ -1369,6 +1368,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 +1426,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)
@@ -1874,29 +1880,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
{
return;
}
#region Deprecate July 2012
//region_ID, regionid, region_uuid will be deprecated in July 2012!!!!!!
else if (requestData.ContainsKey("regionid") &&
!String.IsNullOrEmpty((string)requestData["regionid"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter regionid will be deprecated as of July 2012. Use region_id instead");
}
else if (requestData.ContainsKey("region_ID") &&
!String.IsNullOrEmpty((string)requestData["region_ID"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter region_ID will be deprecated as of July 2012. Use region_id instead");
}
else if (requestData.ContainsKey("regionID") &&
!String.IsNullOrEmpty((string)requestData["regionID"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter regionID will be deprecated as of July 2012. Use region_id instead");
}
else if (requestData.ContainsKey("region_uuid") &&
!String.IsNullOrEmpty((string)requestData["region_uuid"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter region_uuid will be deprecated as of July 2012. Use region_id instead");
}
#endregion
else
{
responseData["accepted"] = false;
@@ -1918,56 +1901,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
#region Deprecate July 2012
else if (requestData.ContainsKey("regionid") &&
!String.IsNullOrEmpty((string)requestData["regionid"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter regionid will be deprecated as of July 2012. Use region_id instead");
UUID regionID = (UUID)(string)requestData["regionid"];
if (!m_application.SceneManager.TryGetScene(regionID, out scene))
{
responseData["error"] = String.Format("Region ID {0} not found", regionID);
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
else if (requestData.ContainsKey("region_ID") &&
!String.IsNullOrEmpty((string)requestData["region_ID"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter region_ID will be deprecated as of July 2012. Use region_id instead");
UUID regionID = (UUID)(string)requestData["region_ID"];
if (!m_application.SceneManager.TryGetScene(regionID, out scene))
{
responseData["error"] = String.Format("Region ID {0} not found", regionID);
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
else if (requestData.ContainsKey("regionID") &&
!String.IsNullOrEmpty((string)requestData["regionID"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter regionID will be deprecated as of July 2012. Use region_id instead");
UUID regionID = (UUID)(string)requestData["regionID"];
if (!m_application.SceneManager.TryGetScene(regionID, out scene))
{
responseData["error"] = String.Format("Region ID {0} not found", regionID);
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
else if (requestData.ContainsKey("region_uuid") &&
!String.IsNullOrEmpty((string)requestData["region_uuid"]))
{
m_log.WarnFormat("[RADMIN]: Use of parameter region_uuid will be deprecated as of July 2012. Use region_id instead");
UUID regionID = (UUID)(string)requestData["region_uuid"];
if (!m_application.SceneManager.TryGetScene(regionID, out scene))
{
responseData["error"] = String.Format("Region ID {0} not found", regionID);
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
#endregion
else if (requestData.ContainsKey("region_name") &&
!String.IsNullOrEmpty((string)requestData["region_name"]))
{

View File

@@ -312,14 +312,16 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// Now that everything is setup we can proceed to
// add THIS agent to the HTTP server's handler list
if (!AddAgentHandler(Rest.Name,this))
{
Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
foreach (IRest handler in handlers)
{
handler.Close();
}
}
// FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
// have to be handled through the AddHttpHandler interface.
// if (!AddAgentHandler(Rest.Name,this))
// {
// Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
// foreach (IRest handler in handlers)
// {
// handler.Close();
// }
// }
}
catch (Exception e)
@@ -342,11 +344,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
try
{
RemoveAgentHandler(Rest.Name, this);
}
catch (KeyNotFoundException){}
// FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
// have to be handled through the AddHttpHandler interface.
// try
// {
// RemoveAgentHandler(Rest.Name, this);
// }
// catch (KeyNotFoundException){}
foreach (IRest handler in handlers)
{

View File

@@ -297,7 +297,9 @@ namespace OpenSim.ApplicationPlugins.Rest
{
if (!IsEnabled) return false;
_agents.Add(agentName, handler);
return _httpd.AddAgentHandler(agentName, handler);
// return _httpd.AddAgentHandler(agentName, handler);
return false;
}
/// <summary>
@@ -316,7 +318,7 @@ namespace OpenSim.ApplicationPlugins.Rest
if (_agents[agentName] == handler)
{
_agents.Remove(agentName);
return _httpd.RemoveAgentHandler(agentName, handler);
// return _httpd.RemoveAgentHandler(agentName, handler);
}
return false;
}
@@ -358,10 +360,10 @@ namespace OpenSim.ApplicationPlugins.Rest
_httpd.RemoveStreamHandler(h.HttpMethod, h.Path);
}
_handlers = null;
foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
{
_httpd.RemoveAgentHandler(h.Key, h.Value);
}
// foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
// {
// _httpd.RemoveAgentHandler(h.Key, h.Value);
// }
_agents = 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

@@ -17,3 +17,8 @@ CREATE TABLE `GridUser` (
) ENGINE=InnoDB;
COMMIT;
:VERSION 2 # --------------------------
BEGIN;
COMMIT;

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Nini.Config;
using log4net;
using OpenMetaverse;
namespace OpenSim.Framework
{
public class AssetPermissions
{
private static readonly ILog m_log =
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
private bool[] m_DisallowExport, m_DisallowImport;
private string[] m_AssetTypeNames;
public AssetPermissions(IConfig config)
{
Type enumType = typeof(AssetType);
m_AssetTypeNames = Enum.GetNames(enumType);
for (int i = 0; i < m_AssetTypeNames.Length; i++)
m_AssetTypeNames[i] = m_AssetTypeNames[i].ToLower();
int n = Enum.GetValues(enumType).Length;
m_DisallowExport = new bool[n];
m_DisallowImport = new bool[n];
LoadPermsFromConfig(config, "DisallowExport", m_DisallowExport);
LoadPermsFromConfig(config, "DisallowImport", m_DisallowImport);
}
private void LoadPermsFromConfig(IConfig assetConfig, string variable, bool[] bitArray)
{
if (assetConfig == null)
return;
string perms = assetConfig.GetString(variable, String.Empty);
string[] parts = perms.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in parts)
{
int index = Array.IndexOf(m_AssetTypeNames, s.Trim().ToLower());
if (index >= 0)
bitArray[index] = true;
else
m_log.WarnFormat("[Asset Permissions]: Invalid AssetType {0}", s);
}
}
public bool AllowedExport(sbyte type)
{
string assetTypeName = ((AssetType)type).ToString();
int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower());
if (index >= 0 && m_DisallowExport[index])
{
m_log.DebugFormat("[Asset Permissions]: Export denied: configuration does not allow export of AssetType {0}", assetTypeName);
return false;
}
return true;
}
public bool AllowedImport(sbyte type)
{
string assetTypeName = ((AssetType)type).ToString();
int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower());
if (index >= 0 && m_DisallowImport[index])
{
m_log.DebugFormat("[Asset Permissions]: Import denied: configuration does not allow import of AssetType {0}", assetTypeName);
return false;
}
return true;
}
}
}

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

@@ -355,10 +355,19 @@ Asset service request failures: {3}" + Environment.NewLine,
sb.Append(Environment.NewLine);
sb.Append(
string.Format(
"{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}",
"{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}\n\n",
inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
sb.Append(Environment.NewLine);
foreach (KeyValuePair<string, Stat> kvp in StatsManager.RegisteredStats)
{
Stat stat = kvp.Value;
if (stat.Category == "scene" && stat.Verbosity == StatVerbosity.Info)
{
sb.AppendFormat("Slow frames ({0}): {1}\n", stat.Container, stat.Value);
}
}
/*
sb.Append(Environment.NewLine);

View File

@@ -25,6 +25,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
namespace OpenSim.Framework.Monitoring
{
/// <summary>
@@ -32,6 +35,14 @@ namespace OpenSim.Framework.Monitoring
/// </summary>
public class StatsManager
{
/// <summary>
/// Registered stats.
/// </summary>
/// <remarks>
/// Do not add or remove from this dictionary.
/// </remarks>
public static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
private static AssetStatsCollector assetStats;
private static UserStatsCollector userStats;
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
@@ -61,5 +72,108 @@ namespace OpenSim.Framework.Monitoring
return userStats;
}
public static bool RegisterStat(Stat stat)
{
lock (RegisteredStats)
{
if (RegisteredStats.ContainsKey(stat.UniqueName))
{
// XXX: For now just return false. This is to avoid problems in regression tests where all tests
// in a class are run in the same instance of the VM.
return false;
// throw new Exception(
// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category);
}
// We take a replace-on-write approach here so that we don't need to generate a new Dictionary
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
newRegisteredStats[stat.UniqueName] = stat;
RegisteredStats = newRegisteredStats;
}
return true;
}
public static bool DeregisterStat(Stat stat)
{
lock (RegisteredStats)
{
if (!RegisteredStats.ContainsKey(stat.UniqueName))
return false;
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
newRegisteredStats.Remove(stat.UniqueName);
RegisteredStats = newRegisteredStats;
return true;
}
}
}
/// <summary>
/// Verbosity of stat.
/// </summary>
/// <remarks>
/// Info will always be displayed.
/// </remarks>
public enum StatVerbosity
{
Debug,
Info
}
/// <summary>
/// Holds individual static details
/// </summary>
public class Stat
{
/// <summary>
/// Unique stat name used for indexing. Each ShortName in a Category must be unique.
/// </summary>
public string UniqueName { get; private set; }
/// <summary>
/// Category of this stat (e.g. cache, scene, etc).
/// </summary>
public string Category { get; private set; }
/// <summary>
/// Containing name for this stat.
/// FIXME: In the case of a scene, this is currently the scene name (though this leaves
/// us with a to-be-resolved problem of non-unique region names).
/// </summary>
/// <value>
/// The container.
/// </value>
public string Container { get; private set; }
public StatVerbosity Verbosity { get; private set; }
public string ShortName { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public string UnitName { get; private set; }
public double Value { get; set; }
public Stat(
string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description)
{
ShortName = shortName;
Name = name;
UnitName = unitName;
Category = category;
Container = container;
Verbosity = verbosity;
Description = description;
UniqueName = GenUniqueName(Container, Category, ShortName);
}
public static string GenUniqueName(string container, string category, string shortName)
{
return string.Format("{0}+{1}+{2}", container, category, shortName);
}
}
}

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; }
@@ -637,11 +643,11 @@ namespace OpenSim.Framework
#region Prim stuff
m_nonphysPrimMin = config.GetFloat("NonphysicalPrimMin", 0);
allKeys.Remove("NonphysicalPrimMin");
m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0);
allKeys.Remove("NonPhysicalPrimMin");
m_nonphysPrimMax = config.GetInt("NonphysicalPrimMax", 0);
allKeys.Remove("NonphysicalPrimMax");
m_nonphysPrimMax = config.GetInt("NonPhysicalPrimMax", 0);
allKeys.Remove("NonPhysicalPrimMax");
m_physPrimMin = config.GetFloat("PhysicalPrimMin", 0);
allKeys.Remove("PhysicalPrimMin");
@@ -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

@@ -54,8 +54,23 @@ namespace OpenSim.Framework.Servers.HttpServer
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
/// <summary>
/// Gets or sets the debug level.
/// </summary>
/// <value>
/// See MainServer.DebugLevel.
/// </value>
public int DebugLevel { get; set; }
/// <summary>
/// Request number for diagnostic purposes.
/// </summary>
/// <remarks>
/// This is an internal number. In some debug situations an external number may also be supplied in the
/// opensim-request-id header but we are not currently logging this.
/// </remarks>
public int RequestNumber { get; private set; }
private volatile int NotSocketErrors = 0;
public volatile bool HTTPDRunning = false;
@@ -67,7 +82,7 @@ namespace OpenSim.Framework.Servers.HttpServer
protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
// protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
new Dictionary<string, PollServiceEventArgs>();
@@ -245,29 +260,29 @@ namespace OpenSim.Framework.Servers.HttpServer
return new List<string>(m_pollHandlers.Keys);
}
// Note that the agent string is provided simply to differentiate
// the handlers - it is NOT required to be an actual agent header
// value.
public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
{
lock (m_agentHandlers)
{
if (!m_agentHandlers.ContainsKey(agent))
{
m_agentHandlers.Add(agent, handler);
return true;
}
}
//must already have a handler for that path so return false
return false;
}
public List<string> GetAgentHandlerKeys()
{
lock (m_agentHandlers)
return new List<string>(m_agentHandlers.Keys);
}
// // Note that the agent string is provided simply to differentiate
// // the handlers - it is NOT required to be an actual agent header
// // value.
// public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
// {
// lock (m_agentHandlers)
// {
// if (!m_agentHandlers.ContainsKey(agent))
// {
// m_agentHandlers.Add(agent, handler);
// return true;
// }
// }
//
// //must already have a handler for that path so return false
// return false;
// }
//
// public List<string> GetAgentHandlerKeys()
// {
// lock (m_agentHandlers)
// return new List<string>(m_agentHandlers.Keys);
// }
public bool AddLLSDHandler(string path, LLSDMethod handler)
{
@@ -296,6 +311,8 @@ namespace OpenSim.Framework.Servers.HttpServer
private void OnRequest(object source, RequestEventArgs args)
{
RequestNumber++;
try
{
IHttpClientContext context = (IHttpClientContext)source;
@@ -405,7 +422,6 @@ namespace OpenSim.Framework.Servers.HttpServer
string requestMethod = request.HttpMethod;
string uriString = request.RawUrl;
// string reqnum = "unknown";
int requestStartTick = Environment.TickCount;
// Will be adjusted later on.
@@ -422,22 +438,22 @@ namespace OpenSim.Framework.Servers.HttpServer
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
// This is the REST agent interface. We require an agent to properly identify
// itself. If the REST handler recognizes the prefix it will attempt to
// satisfy the request. If it is not recognizable, and no damage has occurred
// the request can be passed through to the other handlers. This is a low
// probability event; if a request is matched it is normally expected to be
// handled
IHttpAgentHandler agentHandler;
if (TryGetAgentHandler(request, response, out agentHandler))
{
if (HandleAgentRequest(agentHandler, request, response))
{
requestEndTick = Environment.TickCount;
return;
}
}
// // This is the REST agent interface. We require an agent to properly identify
// // itself. If the REST handler recognizes the prefix it will attempt to
// // satisfy the request. If it is not recognizable, and no damage has occurred
// // the request can be passed through to the other handlers. This is a low
// // probability event; if a request is matched it is normally expected to be
// // handled
// IHttpAgentHandler agentHandler;
//
// if (TryGetAgentHandler(request, response, out agentHandler))
// {
// if (HandleAgentRequest(agentHandler, request, response))
// {
// requestEndTick = Environment.TickCount;
// return;
// }
// }
//response.KeepAlive = true;
response.SendChunked = false;
@@ -449,9 +465,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.
@@ -531,8 +545,8 @@ namespace OpenSim.Framework.Servers.HttpServer
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);
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint);
buffer = HandleHTTPRequest(request, response);
break;
@@ -543,8 +557,8 @@ namespace OpenSim.Framework.Servers.HttpServer
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);
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint);
buffer = HandleLLSDRequests(request, response);
break;
@@ -563,9 +577,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 +585,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);
@@ -628,11 +636,11 @@ namespace OpenSim.Framework.Servers.HttpServer
}
catch (IOException e)
{
m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.Message), e);
m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
}
catch (Exception e)
{
m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.Message), e);
m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
SendHTML500(response);
}
finally
@@ -640,17 +648,93 @@ namespace OpenSim.Framework.Servers.HttpServer
// Every month or so this will wrap and give bad numbers, not really a problem
// since its just for reporting
int tickdiff = requestEndTick - requestStartTick;
if (tickdiff > 3000)
if (tickdiff > 3000 && requestHandler.Name != "GetTexture")
{
m_log.InfoFormat(
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} from {4} took {5}ms",
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
RequestNumber,
requestMethod,
uriString,
requestHandler != null ? requestHandler.Name : "",
requestHandler != null ? requestHandler.Description : "",
request.RemoteIPEndPoint.ToString(),
request.RemoteIPEndPoint,
tickdiff);
}
else if (DebugLevel >= 4)
{
m_log.DebugFormat(
"[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms",
RequestNumber,
Port,
tickdiff);
}
}
}
private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
{
m_log.DebugFormat(
"[BASE HTTP SERVER]: HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}",
RequestNumber,
Port,
request.HttpMethod,
request.Url.PathAndQuery,
requestHandler.Name,
requestHandler.Description,
request.RemoteIPEndPoint);
if (DebugLevel >= 5)
LogIncomingInDetail(request);
}
private void LogIncomingToContentTypeHandler(OSHttpRequest request)
{
m_log.DebugFormat(
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
RequestNumber,
Port,
request.ContentType,
request.HttpMethod,
request.Url.PathAndQuery,
request.RemoteIPEndPoint);
if (DebugLevel >= 5)
LogIncomingInDetail(request);
}
private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
{
m_log.DebugFormat(
"[BASE HTTP SERVER]: HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}",
RequestNumber,
Port,
request.HttpMethod,
request.Url.PathAndQuery,
request.RemoteIPEndPoint);
if (DebugLevel >= 5)
LogIncomingInDetail(request);
}
private void LogIncomingInDetail(OSHttpRequest request)
{
using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8))
{
string output;
if (DebugLevel == 5)
{
const int sampleLength = 80;
char[] sampleChars = new char[sampleLength];
reader.Read(sampleChars, 0, sampleLength);
output = new string(sampleChars);
}
else
{
output = reader.ReadToEnd();
}
m_log.DebugFormat("[BASE HTTP SERVER]: {0}...", output.Replace("\n", @"\n"));
}
}
@@ -746,24 +830,24 @@ namespace OpenSim.Framework.Servers.HttpServer
}
}
private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
{
agentHandler = null;
lock (m_agentHandlers)
{
foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
{
if (handler.Match(request, response))
{
agentHandler = handler;
return true;
}
}
}
return false;
}
// private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
// {
// agentHandler = null;
//
// lock (m_agentHandlers)
// {
// foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
// {
// if (handler.Match(request, response))
// {
// agentHandler = handler;
// return true;
// }
// }
// }
//
// return false;
// }
/// <summary>
/// Try all the registered xmlrpc handlers when an xmlrpc request is received.
@@ -1688,21 +1772,21 @@ namespace OpenSim.Framework.Servers.HttpServer
m_pollHandlers.Remove(path);
}
public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
{
lock (m_agentHandlers)
{
IHttpAgentHandler foundHandler;
if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
{
m_agentHandlers.Remove(agent);
return true;
}
}
return false;
}
// public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
// {
// lock (m_agentHandlers)
// {
// IHttpAgentHandler foundHandler;
//
// if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
// {
// m_agentHandlers.Remove(agent);
// return true;
// }
// }
//
// return false;
// }
public void RemoveXmlRPCHandler(string method)
{

View File

@@ -41,10 +41,10 @@ namespace OpenSim.Framework.Servers.HttpServer
uint Port { get; }
bool UseSSL { get; }
// Note that the agent string is provided simply to differentiate
// the handlers - it is NOT required to be an actual agent header
// value.
bool AddAgentHandler(string agent, IHttpAgentHandler handler);
// // Note that the agent string is provided simply to differentiate
// // the handlers - it is NOT required to be an actual agent header
// // value.
// bool AddAgentHandler(string agent, IHttpAgentHandler handler);
/// <summary>
/// Add a handler for an HTTP request.
@@ -106,13 +106,13 @@ namespace OpenSim.Framework.Servers.HttpServer
bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
/// <summary>
/// Remove the agent if it is registered.
/// </summary>
/// <param name="agent"></param>
/// <param name="handler"></param>
/// <returns></returns>
bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
// /// <summary>
// /// Remove the agent if it is registered.
// /// </summary>
// /// <param name="agent"></param>
// /// <param name="handler"></param>
// /// <returns></returns>
// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
/// <summary>
/// Remove an HTTP handler

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;
@@ -47,9 +48,12 @@ namespace OpenSim.Framework.Servers
/// Control the printing of certain debug messages.
/// </summary>
/// <remarks>
/// If DebugLevel >= 1, then short warnings are logged when receiving bad input data.
/// If DebugLevel >= 2, then long warnings are logged when receiving bad input data.
/// If DebugLevel >= 3, then short notices about all incoming non-poll HTTP requests are logged.
/// If DebugLevel >= 1 then short warnings are logged when receiving bad input data.
/// If DebugLevel >= 2 then long warnings are logged when receiving bad input data.
/// If DebugLevel >= 3 then short notices about all incoming non-poll HTTP requests are logged.
/// If DebugLevel >= 4 then the time taken to fulfill the request is logged.
/// If DebugLevel >= 5 then the start of the body of incoming non-poll HTTP requests will be logged.
/// If DebugLevel >= 6 then the entire body of incoming non-poll HTTP requests will be logged.
/// </remarks>
public static int DebugLevel
{
@@ -101,17 +105,28 @@ namespace OpenSim.Framework.Servers
get { return new Dictionary<uint, BaseHttpServer>(m_Servers); }
}
public static void RegisterHttpConsoleCommands(ICommandConsole console)
{
console.Commands.AddCommand(
"Debug", false, "debug http", "debug http [<level>]",
"Turn on inbound non-poll http request debugging.",
"If level <= 0, then no extra logging is done.\n"
+ "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 no level is specified then the current level is returned.",
"Comms", false, "show http-handlers",
"show http-handlers",
"Show all registered http handlers", HandleShowHttpHandlersCommand);
console.Commands.AddCommand(
"Debug", false, "debug http", "debug http <in|out|all> [<level>]",
"Turn on http request logging.",
"If in or all and\n"
+ " level <= 0 then no extra logging is done.\n"
+ " level >= 1 then short warnings are logged when receiving bad input data.\n"
+ " level >= 2 then long warnings are logged when receiving bad input data.\n"
+ " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n"
+ " level >= 4 then the time taken to fulfill the request is logged.\n"
+ " level >= 5 then a sample from the beginning of the incoming data is logged.\n"
+ " level >= 6 then the entire incoming data is logged.\n"
+ " no level is specified then the current level is returned.\n\n"
+ "If out or all and\n"
+ " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n"
+ " level >= 4 then the time taken to fulfill the request is logged.\n",
HandleDebugHttpCommand);
}
@@ -119,25 +134,120 @@ namespace OpenSim.Framework.Servers
/// Turn on some debugging values for OpenSim.
/// </summary>
/// <param name="args"></param>
private static void HandleDebugHttpCommand(string module, string[] args)
private static void HandleDebugHttpCommand(string module, string[] cmdparams)
{
if (args.Length == 3)
if (cmdparams.Length < 3)
{
int newDebug;
if (int.TryParse(args[2], out newDebug))
{
MainServer.DebugLevel = newDebug;
MainConsole.Instance.OutputFormat("Debug http level set to {0}", newDebug);
}
MainConsole.Instance.Output("Usage: debug http <in|out|all> 0..6");
return;
}
else if (args.Length == 2)
bool inReqs = false;
bool outReqs = false;
bool allReqs = false;
string subCommand = cmdparams[2];
if (subCommand.ToLower() == "in")
{
MainConsole.Instance.OutputFormat("Current debug http level is {0}", MainServer.DebugLevel);
inReqs = true;
}
else if (subCommand.ToLower() == "out")
{
outReqs = true;
}
else if (subCommand.ToLower() == "all")
{
allReqs = true;
}
else
{
MainConsole.Instance.Output("Usage: debug http 0..3");
MainConsole.Instance.Output("You must specify in, out or all");
return;
}
if (cmdparams.Length >= 4)
{
string rawNewDebug = cmdparams[3];
int newDebug;
if (!int.TryParse(rawNewDebug, out newDebug))
{
MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawNewDebug);
return;
}
if (newDebug < 0 || newDebug > 6)
{
MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0..6", newDebug);
return;
}
if (allReqs || inReqs)
{
MainServer.DebugLevel = newDebug;
MainConsole.Instance.OutputFormat("IN debug level set to {0}", newDebug);
}
if (allReqs || outReqs)
{
WebUtil.DebugLevel = newDebug;
MainConsole.Instance.OutputFormat("OUT debug level set to {0}", newDebug);
}
}
else
{
if (allReqs || inReqs)
MainConsole.Instance.OutputFormat("Current IN debug level is {0}", MainServer.DebugLevel);
if (allReqs || outReqs)
MainConsole.Instance.OutputFormat("Current OUT debug level is {0}", WebUtil.DebugLevel);
}
}
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>

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

@@ -53,10 +53,18 @@ namespace OpenSim.Framework
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Control the printing of certain debug messages.
/// </summary>
/// <remarks>
/// If DebugLevel >= 3 then short notices about outgoing HTTP requests are logged.
/// </remarks>
public static int DebugLevel { get; set; }
/// <summary>
/// Request number for diagnostic purposes.
/// </summary>
public static int RequestNumber = 0;
public static int RequestNumber { get; internal set; }
/// <summary>
/// this is the header field used to communicate the local request id
@@ -146,7 +154,11 @@ namespace OpenSim.Framework
private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed)
{
int reqnum = RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
if (DebugLevel >= 3)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} ServiceOSD {1} {2} (timeout {3}, compressed {4})",
reqnum, method, url, timeout, compressed);
string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount();
@@ -229,7 +241,7 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime)
m_log.InfoFormat(
"[OSD REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
"[WEB UTIL]: Slow ServiceOSD request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
@@ -238,10 +250,14 @@ namespace OpenSim.Framework
strBuffer != null
? (strBuffer.Length > MaxRequestDiagLength ? strBuffer.Remove(MaxRequestDiagLength) : strBuffer)
: "");
else if (DebugLevel >= 4)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
reqnum, tickdiff, tickdata);
}
m_log.DebugFormat(
"[WEB UTIL]: <{0}> osd request for {1}, method {2} FAILED: {3}", reqnum, url, method, errorMessage);
"[WEB UTIL]: ServiceOSD request {0} {1} {2} FAILED: {3}", reqnum, url, method, errorMessage);
return ErrorResponseMap(errorMessage);
}
@@ -317,7 +333,11 @@ namespace OpenSim.Framework
{
int reqnum = RequestNumber++;
string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown";
// m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method);
if (DebugLevel >= 3)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} ServiceForm {1} {2} (timeout {3})",
reqnum, method, url, timeout);
string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount();
@@ -380,7 +400,7 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime)
m_log.InfoFormat(
"[SERVICE FORM]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
"[WEB UTIL]: Slow ServiceForm request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
@@ -389,9 +409,13 @@ namespace OpenSim.Framework
queryString != null
? (queryString.Length > MaxRequestDiagLength) ? queryString.Remove(MaxRequestDiagLength) : queryString
: "");
else if (DebugLevel >= 4)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
reqnum, tickdiff, tickdata);
}
m_log.WarnFormat("[SERVICE FORM]: <{0}> form request to {1} failed: {2}", reqnum, url, errorMessage);
m_log.WarnFormat("[WEB UTIL]: ServiceForm request {0} {1} {2} failed: {2}", reqnum, method, url, errorMessage);
return ErrorResponseMap(errorMessage);
}
@@ -643,7 +667,6 @@ namespace OpenSim.Framework
/// <returns></returns>
public static string[] GetPreferredImageTypes(string accept)
{
if (accept == null || accept == string.Empty)
return new string[0];
@@ -693,18 +716,31 @@ namespace OpenSim.Framework
//
public static void MakeRequest<TRequest, TResponse>(string verb,
string requestUrl, TRequest obj, Action<TResponse> action)
{
MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, action, 0);
}
public static void MakeRequest<TRequest, TResponse>(string verb,
string requestUrl, TRequest obj, Action<TResponse> action,
int maxConnections)
{
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
if (WebUtil.DebugLevel >= 3)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} AsynchronousRequestObject {1} {2}",
reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
// m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl);
Type type = typeof(TRequest);
WebRequest request = WebRequest.Create(requestUrl);
HttpWebRequest ht = (HttpWebRequest)request;
if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
ht.ServicePoint.ConnectionLimit = maxConnections;
WebResponse response = null;
TResponse deserial = default(TResponse);
XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
@@ -854,7 +890,7 @@ namespace OpenSim.Framework
}
m_log.InfoFormat(
"[ASYNC REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
"[ASYNC REQUEST]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
@@ -862,6 +898,12 @@ namespace OpenSim.Framework
tickdata,
originalRequest);
}
else if (WebUtil.DebugLevel >= 4)
{
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
reqnum, tickdiff, tickdata);
}
}
}
@@ -882,7 +924,11 @@ namespace OpenSim.Framework
public static string MakeRequest(string verb, string requestUrl, string obj)
{
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
if (WebUtil.DebugLevel >= 3)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} SynchronousRestForms {1} {2}",
reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
@@ -967,13 +1013,17 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > WebUtil.LongCallTime)
m_log.InfoFormat(
"[FORMS]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
"[FORMS]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
tickdiff,
tickdata,
obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
else if (WebUtil.DebugLevel >= 4)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
reqnum, tickdiff, tickdata);
return respstring;
}
@@ -996,9 +1046,23 @@ namespace OpenSim.Framework
/// <exception cref="System.Net.WebException">Thrown if we encounter a network issue while posting
/// the request. You'll want to make sure you deal with this as they're not uncommon</exception>
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj)
{
return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, 0);
}
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout)
{
return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, pTimeout, 0);
}
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections)
{
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
if (WebUtil.DebugLevel >= 3)
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} SynchronousRestObject {1} {2}",
reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
@@ -1007,6 +1071,10 @@ namespace OpenSim.Framework
TResponse deserial = default(TResponse);
WebRequest request = WebRequest.Create(requestUrl);
HttpWebRequest ht = (HttpWebRequest)request;
if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
ht.ServicePoint.ConnectionLimit = maxConnections;
request.Method = verb;
MemoryStream buffer = null;
@@ -1111,7 +1179,7 @@ namespace OpenSim.Framework
}
m_log.InfoFormat(
"[SynchronousRestObjectRequester]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
"[SynchronousRestObjectRequester]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
@@ -1119,8 +1187,14 @@ namespace OpenSim.Framework
tickdata,
originalRequest);
}
else if (WebUtil.DebugLevel >= 4)
{
m_log.DebugFormat(
"[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
reqnum, tickdiff, tickdata);
}
return deserial;
}
}
}
}

View File

@@ -27,6 +27,7 @@
using System;
using System.IO;
using System.Net;
using System.Reflection;
using log4net;
using log4net.Config;
@@ -73,6 +74,7 @@ namespace OpenSim
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
ServicePointManager.DefaultConnectionLimit = 12;
// Add the arguments supplied when running the application to the configuration
ArgvConfigSource configSource = new ArgvConfigSource(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

@@ -57,39 +57,36 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
/// <summary>
/// Return a xfer uploader if one does not already exist.
/// Return the xfer uploader for the given transaction.
/// </summary>
/// <remarks>
/// If an uploader does not already exist for this transaction then it is created, otherwise the existing
/// uploader is returned.
/// </remarks>
/// <param name="transactionID"></param>
/// <param name="assetID">
/// We must transfer the new asset ID into the uploader on creation, otherwise
/// we can see race conditions with other threads which can retrieve an item before it is updated with the new
/// asset id.
/// </param>
/// <returns>
/// The xfer uploader requested. Null if one is already in existence.
/// FIXME: This is a bizarre thing to do, and is probably meant to signal an error condition if multiple
/// transfers are made. Needs to be corrected.
/// </returns>
public AssetXferUploader RequestXferUploader(UUID transactionID, UUID assetID)
/// <returns>The asset xfer uploader</returns>
public AssetXferUploader RequestXferUploader(UUID transactionID)
{
AssetXferUploader uploader;
lock (XferUploaders)
{
if (!XferUploaders.ContainsKey(transactionID))
{
AssetXferUploader uploader = new AssetXferUploader(this, m_Scene, assetID, m_dumpAssetsToFile);
uploader = new AssetXferUploader(this, m_Scene, transactionID, m_dumpAssetsToFile);
// m_log.DebugFormat(
// "[AGENT ASSETS TRANSACTIONS]: Adding asset xfer uploader {0} since it didn't previously exist", transactionID);
XferUploaders.Add(transactionID, uploader);
return uploader;
}
else
{
uploader = XferUploaders[transactionID];
}
}
m_log.WarnFormat("[AGENT ASSETS TRANSACTIONS]: Ignoring request for asset xfer uploader {0} since it already exists", transactionID);
return null;
return uploader;
}
public void HandleXfer(ulong xferID, uint packetID, byte[] data)
@@ -151,115 +148,28 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
string description, string name, sbyte invType,
sbyte type, byte wearableType, uint nextOwnerMask)
{
AssetXferUploader uploader = null;
AssetXferUploader uploader = RequestXferUploader(transactionID);
lock (XferUploaders)
{
if (XferUploaders.ContainsKey(transactionID))
uploader = XferUploaders[transactionID];
}
if (uploader != null)
uploader.RequestCreateInventoryItem(
remoteClient, transactionID, folderID,
callbackID, description, name, invType, type,
wearableType, nextOwnerMask);
else
m_log.ErrorFormat(
"[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to create inventory item {1} from {2}",
transactionID, name, remoteClient.Name);
}
/// <summary>
/// Get an uploaded asset. If the data is successfully retrieved,
/// the transaction will be removed.
/// </summary>
/// <param name="transactionID"></param>
/// <returns>The asset if the upload has completed, null if it has not.</returns>
private AssetBase GetTransactionAsset(UUID transactionID)
{
lock (XferUploaders)
{
if (XferUploaders.ContainsKey(transactionID))
{
AssetXferUploader uploader = XferUploaders[transactionID];
AssetBase asset = uploader.GetAssetData();
RemoveXferUploader(transactionID);
return asset;
}
}
return null;
uploader.RequestCreateInventoryItem(
remoteClient, folderID, callbackID,
description, name, invType, type, wearableType, nextOwnerMask);
}
public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient,
SceneObjectPart part, UUID transactionID,
TaskInventoryItem item)
{
AssetXferUploader uploader = null;
AssetXferUploader uploader = RequestXferUploader(transactionID);
lock (XferUploaders)
{
if (XferUploaders.ContainsKey(transactionID))
uploader = XferUploaders[transactionID];
}
if (uploader != null)
{
AssetBase asset = GetTransactionAsset(transactionID);
// Only legacy viewers use this, and they prefer CAPS, which
// we have, so this really never runs.
// Allow it, but only for "safe" types.
if ((InventoryType)item.InvType != InventoryType.Notecard &&
(InventoryType)item.InvType != InventoryType.LSL)
return;
if (asset != null)
{
// m_log.DebugFormat(
// "[AGENT ASSETS TRANSACTIONS]: Updating item {0} in {1} for transaction {2}",
// item.Name, part.Name, transactionID);
asset.FullID = UUID.Random();
asset.Name = item.Name;
asset.Description = item.Description;
asset.Type = (sbyte)item.Type;
item.AssetID = asset.FullID;
m_Scene.AssetService.Store(asset);
}
}
else
{
m_log.ErrorFormat(
"[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to update task inventory item {1} in {2}",
transactionID, item.Name, part.Name);
}
uploader.RequestUpdateTaskInventoryItem(remoteClient, item);
}
public void RequestUpdateInventoryItem(IClientAPI remoteClient,
UUID transactionID, InventoryItemBase item)
{
AssetXferUploader uploader = null;
AssetXferUploader uploader = RequestXferUploader(transactionID);
lock (XferUploaders)
{
if (XferUploaders.ContainsKey(transactionID))
uploader = XferUploaders[transactionID];
}
if (uploader != null)
{
uploader.RequestUpdateInventoryItem(remoteClient, transactionID, item);
}
else
{
m_log.ErrorFormat(
"[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to update inventory item {1} for {2}",
transactionID, item.Name, remoteClient.Name);
}
uploader.RequestUpdateInventoryItem(remoteClient, item);
}
}
}
}

View File

@@ -215,7 +215,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
{
m_log.DebugFormat(
"[TRANSACTIONS MANAGER] Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}",
"[ASSET TRANSACTION MODULE] Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}",
item.Name, part.Name, remoteClient.Name, m_Scene.RegionInfo.RegionName);
AgentAssetTransactions transactions =
@@ -274,13 +274,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
AssetXferUploader uploader = transactions.RequestXferUploader(transaction, assetID);
if (uploader != null)
{
uploader.Initialise(remoteClient, assetID, transaction, type,
data, storeLocal, tempFile);
}
AssetXferUploader uploader = transactions.RequestXferUploader(transaction);
uploader.StartUpload(remoteClient, assetID, transaction, type, data, storeLocal, tempFile);
}
/// <summary>

View File

@@ -40,39 +40,75 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Upload state.
/// </summary>
/// <remarks>
/// New -> Uploading -> Complete
/// </remarks>
private enum UploadState
{
New,
Uploading,
Complete
}
/// <summary>
/// Reference to the object that holds this uploader. Used to remove ourselves from it's list if we
/// are performing a delayed update.
/// </summary>
AgentAssetTransactions m_transactions;
private UploadState m_uploadState = UploadState.New;
private AssetBase m_asset;
private UUID InventFolder = UUID.Zero;
private sbyte invType = 0;
private bool m_createItem = false;
private uint m_createItemCallback = 0;
private bool m_updateItem = false;
private bool m_createItem;
private uint m_createItemCallback;
private bool m_updateItem;
private InventoryItemBase m_updateItemData;
private bool m_updateTaskItem;
private TaskInventoryItem m_updateTaskItemData;
private string m_description = String.Empty;
private bool m_dumpAssetToFile;
private bool m_finished = false;
private string m_name = String.Empty;
private bool m_storeLocal;
// private bool m_storeLocal;
private uint nextPerm = 0;
private IClientAPI ourClient;
private UUID TransactionID = UUID.Zero;
private UUID m_transactionID;
private sbyte type = 0;
private byte wearableType = 0;
public ulong XferID;
private Scene m_Scene;
public AssetXferUploader(AgentAssetTransactions transactions, Scene scene, UUID assetID, bool dumpAssetToFile)
/// <summary>
/// AssetXferUploader constructor
/// </summary>
/// <param name='transactions'>/param>
/// <param name='scene'></param>
/// <param name='transactionID'></param>
/// <param name='dumpAssetToFile'>
/// If true then when the asset is uploaded it is dumped to a file with the format
/// String.Format("{6}_{7}_{0:d2}{1:d2}{2:d2}_{3:d2}{4:d2}{5:d2}.dat",
/// now.Year, now.Month, now.Day, now.Hour, now.Minute,
/// now.Second, m_asset.Name, m_asset.Type);
/// for debugging purposes.
/// </param>
public AssetXferUploader(
AgentAssetTransactions transactions, Scene scene, UUID transactionID, bool dumpAssetToFile)
{
m_asset = new AssetBase();
m_transactions = transactions;
m_transactionID = transactionID;
m_Scene = scene;
m_asset = new AssetBase() { FullID = assetID };
m_dumpAssetToFile = dumpAssetToFile;
}
@@ -118,30 +154,50 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
/// <summary>
/// Initialise asset transfer from the client
/// Start asset transfer from the client
/// </summary>
/// <param name="xferID"></param>
/// <param name="packetID"></param>
/// <param name="data"></param>
public void Initialise(IClientAPI remoteClient, UUID assetID,
UUID transaction, sbyte type, byte[] data, bool storeLocal,
bool tempFile)
/// <param name="remoteClient"></param>
/// <param name="assetID"></param>
/// <param name="transaction"></param>
/// <param name="type"></param>
/// <param name="data">
/// Optional data. If present then the asset is created immediately with this data
/// rather than requesting an upload from the client. The data must be longer than 2 bytes.
/// </param>
/// <param name="storeLocal"></param>
/// <param name="tempFile"></param>
public void StartUpload(
IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data, bool storeLocal,
bool tempFile)
{
// m_log.DebugFormat(
// "[ASSET XFER UPLOADER]: Initialised xfer from {0}, asset {1}, transaction {2}, type {3}, storeLocal {4}, tempFile {5}, already received data length {6}",
// remoteClient.Name, assetID, transaction, type, storeLocal, tempFile, data.Length);
lock (this)
{
if (m_uploadState != UploadState.New)
{
m_log.WarnFormat(
"[ASSET XFER UPLOADER]: Tried to start upload of asset {0}, transaction {1} for {2} but this is already in state {3}. Aborting.",
assetID, transaction, remoteClient.Name, m_uploadState);
return;
}
m_uploadState = UploadState.Uploading;
}
ourClient = remoteClient;
m_asset.Name = "blank";
m_asset.Description = "empty";
m_asset.FullID = assetID;
m_asset.Type = type;
m_asset.CreatorID = remoteClient.AgentId.ToString();
m_asset.Data = data;
m_asset.Local = storeLocal;
m_asset.Temporary = tempFile;
TransactionID = transaction;
m_storeLocal = storeLocal;
// m_storeLocal = storeLocal;
if (m_asset.Data.Length > 2)
{
@@ -166,36 +222,35 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
protected void SendCompleteMessage()
{
ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true,
m_asset.FullID);
// We must lock in order to avoid a race with a separate thread dealing with an inventory item or create
// message from other client UDP.
lock (this)
{
m_finished = true;
m_uploadState = UploadState.Complete;
ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true, m_asset.FullID);
if (m_createItem)
{
DoCreateItem(m_createItemCallback);
CompleteCreateItem(m_createItemCallback);
}
else if (m_updateItem)
{
StoreAssetForItemUpdate(m_updateItemData);
// Remove ourselves from the list of transactions if completion was delayed until the transaction
// was complete.
// TODO: Should probably do the same for create item.
m_transactions.RemoveXferUploader(TransactionID);
CompleteItemUpdate(m_updateItemData);
}
else if (m_storeLocal)
else if (m_updateTaskItem)
{
m_Scene.AssetService.Store(m_asset);
CompleteTaskItemUpdate(m_updateTaskItemData);
}
// else if (m_storeLocal)
// {
// m_Scene.AssetService.Store(m_asset);
// }
}
m_log.DebugFormat(
"[ASSET XFER UPLOADER]: Uploaded asset {0} for transaction {1}",
m_asset.FullID, TransactionID);
m_asset.FullID, m_transactionID);
if (m_dumpAssetToFile)
{
@@ -223,40 +278,37 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
public void RequestCreateInventoryItem(IClientAPI remoteClient,
UUID transactionID, UUID folderID, uint callbackID,
UUID folderID, uint callbackID,
string description, string name, sbyte invType,
sbyte type, byte wearableType, uint nextOwnerMask)
{
if (TransactionID == transactionID)
{
InventFolder = folderID;
m_name = name;
m_description = description;
this.type = type;
this.invType = invType;
this.wearableType = wearableType;
nextPerm = nextOwnerMask;
m_asset.Name = name;
m_asset.Description = description;
m_asset.Type = type;
InventFolder = folderID;
m_name = name;
m_description = description;
this.type = type;
this.invType = invType;
this.wearableType = wearableType;
nextPerm = nextOwnerMask;
m_asset.Name = name;
m_asset.Description = description;
m_asset.Type = type;
// We must lock to avoid a race with a separate thread uploading the asset.
lock (this)
// We must lock to avoid a race with a separate thread uploading the asset.
lock (this)
{
if (m_uploadState == UploadState.Complete)
{
if (m_finished)
{
DoCreateItem(callbackID);
}
else
{
m_createItem = true; //set flag so the inventory item is created when upload is complete
m_createItemCallback = callbackID;
}
CompleteCreateItem(callbackID);
}
else
{
m_createItem = true; //set flag so the inventory item is created when upload is complete
m_createItemCallback = callbackID;
}
}
}
public void RequestUpdateInventoryItem(IClientAPI remoteClient, UUID transactionID, InventoryItemBase item)
public void RequestUpdateInventoryItem(IClientAPI remoteClient, InventoryItemBase item)
{
// We must lock to avoid a race with a separate thread uploading the asset.
lock (this)
@@ -271,9 +323,9 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
item.AssetID = m_asset.FullID;
m_Scene.InventoryService.UpdateItem(item);
if (m_finished)
if (m_uploadState == UploadState.Complete)
{
StoreAssetForItemUpdate(item);
CompleteItemUpdate(item);
}
else
{
@@ -287,20 +339,59 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
}
public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient, TaskInventoryItem taskItem)
{
// We must lock to avoid a race with a separate thread uploading the asset.
lock (this)
{
m_asset.Name = taskItem.Name;
m_asset.Description = taskItem.Description;
m_asset.Type = (sbyte)taskItem.Type;
taskItem.AssetID = m_asset.FullID;
if (m_uploadState == UploadState.Complete)
{
CompleteTaskItemUpdate(taskItem);
}
else
{
m_updateTaskItem = true;
m_updateTaskItemData = taskItem;
}
}
}
/// <summary>
/// Store the asset for the given item.
/// Store the asset for the given item when it has been uploaded.
/// </summary>
/// <param name="item"></param>
private void StoreAssetForItemUpdate(InventoryItemBase item)
private void CompleteItemUpdate(InventoryItemBase item)
{
// m_log.DebugFormat(
// "[ASSET XFER UPLOADER]: Storing asset {0} for earlier item update for {1} for {2}",
// m_asset.FullID, item.Name, ourClient.Name);
m_Scene.AssetService.Store(m_asset);
m_transactions.RemoveXferUploader(m_transactionID);
}
private void DoCreateItem(uint callbackID)
/// <summary>
/// Store the asset for the given task item when it has been uploaded.
/// </summary>
/// <param name="taskItem"></param>
private void CompleteTaskItemUpdate(TaskInventoryItem taskItem)
{
// m_log.DebugFormat(
// "[ASSET XFER UPLOADER]: Storing asset {0} for earlier task item update for {1} for {2}",
// m_asset.FullID, taskItem.Name, ourClient.Name);
m_Scene.AssetService.Store(m_asset);
m_transactions.RemoveXferUploader(m_transactionID);
}
private void CompleteCreateItem(uint callbackID)
{
m_Scene.AssetService.Store(m_asset);
@@ -326,20 +417,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
ourClient.SendInventoryItemCreateUpdate(item, callbackID);
else
ourClient.SendAlertMessage("Unable to create inventory item");
}
/// <summary>
/// Get the asset data uploaded in this transfer.
/// </summary>
/// <returns>null if the asset has not finished uploading</returns>
public AssetBase GetAssetData()
{
if (m_finished)
{
return m_asset;
}
return null;
m_transactions.RemoveXferUploader(m_transactionID);
}
}
}
}

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

@@ -238,8 +238,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
sp.ClearAttachments();
}
public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp)
{
if (!Enabled)
return false;
if (AttachObjectInternal(sp, group, attachmentPt, silent, temp))
{
m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
return true;
}
return false;
}
private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp)
{
lock (sp.AttachmentsSyncLock)
{
@@ -392,6 +406,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
}
public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
{
DetachSingleAttachmentToGround(sp, soLocalId, sp.AbsolutePosition, Quaternion.Identity);
}
public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot)
{
if (!Enabled)
return;
@@ -434,7 +453,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
so.FromItemID = UUID.Zero;
SceneObjectPart rootPart = so.RootPart;
so.AbsolutePosition = sp.AbsolutePosition;
so.AbsolutePosition = absolutePos;
if (absoluteRot != Quaternion.Identity)
{
so.UpdateGroupRotationR(absoluteRot);
}
so.AttachedAvatar = UUID.Zero;
rootPart.SetParentLocalId(0);
so.ClearPartAttachmentData();
@@ -794,7 +817,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
// This will throw if the attachment fails
try
{
AttachObject(sp, objatt, attachmentPt, false, false);
AttachObjectInternal(sp, objatt, attachmentPt, false, false);
}
catch (Exception e)
{
@@ -859,6 +882,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
item = m_scene.InventoryService.GetItem(item);
if (item == null)
return;
bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID);
if (changed && m_scene.AvatarFactory != null)
{
@@ -948,15 +974,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
AttachmentPt &= 0x7f;
// Calls attach with a Zero position
if (AttachObject(sp, part.ParentGroup, AttachmentPt, false, false))
{
// m_log.Debug(
// "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
// + ", AttachmentPoint: " + AttachmentPt);
// Save avatar attachment information
m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.FromItemID, remoteClient.AgentId);
}
AttachObject(sp, part.ParentGroup, AttachmentPt, false, false);
}
catch (Exception e)
{

View File

@@ -62,7 +62,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
public class AttachmentsModuleTests : OpenSimTestCase
{
private AutoResetEvent m_chatEvent = new AutoResetEvent(false);
private OSChatMessage m_osChatMessageReceived;
// private OSChatMessage m_osChatMessageReceived;
// Used to test whether the operations have fired the attach event. Must be reset after each test.
private int m_numberOfAttachEventsFired;
[TestFixtureSetUp]
public void FixtureInit()
@@ -83,7 +86,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
{
// Console.WriteLine("Got chat [{0}]", oscm.Message);
m_osChatMessageReceived = oscm;
// m_osChatMessageReceived = oscm;
m_chatEvent.Set();
}
@@ -99,6 +102,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
"attachments-test-scene", TestHelpers.ParseTail(999), 1000, 1000, config);
SceneHelpers.SetupSceneModules(scene, config, modules.ToArray());
scene.EventManager.OnAttach += (localID, itemID, avatarID) => m_numberOfAttachEventsFired++;
return scene;
}
@@ -181,6 +186,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
m_numberOfAttachEventsFired = 0;
Scene scene = CreateTestScene();
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
@@ -189,6 +196,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, false);
// Check status on scene presence
@@ -216,7 +224,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
// TestHelpers.DisableLogging();
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
/// <summary>
@@ -228,6 +237,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
m_numberOfAttachEventsFired = 0;
Scene scene = CreateTestScene();
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
@@ -247,6 +258,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.HasAttachments(), Is.False);
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -261,6 +275,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20);
m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
@@ -280,6 +295,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
/// <summary>
@@ -338,6 +356,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
ISceneEntity so
= scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.DetachSingleAttachmentToGround(sp, so.LocalId);
// Check scene presence status
@@ -353,6 +373,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
// Check object in scene
Assert.That(scene.GetSceneObjectGroup("att"), Is.Not.Null);
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
[Test]
@@ -369,6 +392,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup so
= (SceneObjectGroup)scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.DetachSingleAttachmentToInv(sp, so);
// Check status on scene presence
@@ -380,6 +405,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo(0));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(0));
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
/// <summary>
@@ -461,10 +489,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup rezzedAtt = presence.GetAttachments()[0];
m_numberOfAttachEventsFired = 0;
scene.IncomingCloseAgent(presence.UUID, false);
// Check that we can't retrieve this attachment from the scene.
Assert.That(scene.GetSceneObjectGroup(rezzedAtt.UUID), Is.Null);
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -480,6 +512,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
AgentCircuitData acd = SceneHelpers.GenerateAgentData(ua1.PrincipalID);
acd.Appearance = new AvatarAppearance();
acd.Appearance.SetAttachment((int)AttachmentPoint.Chest, attItem.ID, attItem.AssetID);
m_numberOfAttachEventsFired = 0;
ScenePresence presence = SceneHelpers.AddScenePresence(scene, acd);
Assert.That(presence.HasAttachments(), Is.True);
@@ -502,6 +536,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(presence.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
// Check events. We expect OnAttach to fire on login.
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
[Test]
@@ -522,10 +559,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Vector3 newPosition = new Vector3(1, 2, 4);
m_numberOfAttachEventsFired = 0;
scene.SceneGraph.UpdatePrimGroupPosition(attSo.LocalId, newPosition, sp.ControllingClient);
Assert.That(attSo.AbsolutePosition, Is.EqualTo(sp.AbsolutePosition));
Assert.That(attSo.RootPart.AttachedPos, Is.EqualTo(newPosition));
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -574,6 +615,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Vector3 teleportPosition = new Vector3(10, 11, 12);
Vector3 teleportLookAt = new Vector3(20, 21, 22);
m_numberOfAttachEventsFired = 0;
sceneA.RequestTeleportLocation(
beforeTeleportSp.ControllingClient,
sceneB.RegionInfo.RegionHandle,
@@ -616,29 +658,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(actualSceneAAttachments.Count, Is.EqualTo(0));
Assert.That(sceneA.GetSceneObjectGroups().Count, Is.EqualTo(0));
}
// I'm commenting this test because scene setup NEEDS InventoryService to
// be non-null
//[Test]
// public void T032_CrossAttachments()
// {
// TestHelpers.InMethod();
//
// ScenePresence presence = scene.GetScenePresence(agent1);
// ScenePresence presence2 = scene2.GetScenePresence(agent1);
// presence2.AddAttachment(sog1);
// presence2.AddAttachment(sog2);
//
// ISharedRegionModule serialiser = new SerialiserModule();
// SceneHelpers.SetupSceneModules(scene, new IniConfigSource(), serialiser);
// SceneHelpers.SetupSceneModules(scene2, new IniConfigSource(), serialiser);
//
// Assert.That(presence.HasAttachments(), Is.False, "Presence has attachments before cross");
//
// //Assert.That(presence2.CrossAttachmentsIntoNewRegion(region1, true), Is.True, "Cross was not successful");
// Assert.That(presence2.HasAttachments(), Is.False, "Presence2 objects were not deleted");
// Assert.That(presence.HasAttachments(), Is.True, "Presence has not received new objects");
// }
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
}
}
}

View File

@@ -525,7 +525,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
{
for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
{
for (int j = 0; j < appearance.Wearables[j].Count; j++)
for (int j = 0; j < appearance.Wearables[i].Count; j++)
{
if (appearance.Wearables[i][j].ItemID == UUID.Zero)
continue;
@@ -533,6 +533,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
// Ignore ruth's assets
if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID)
continue;
InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID);
baseItem = invService.GetItem(baseItem);

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

@@ -297,6 +297,45 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
});
}
}
else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
{
UUID destinationFolderID = UUID.Zero;
if (im.binaryBucket != null && im.binaryBucket.Length >= 16)
{
destinationFolderID = new UUID(im.binaryBucket, 0);
}
if (destinationFolderID != UUID.Zero)
{
IInventoryService invService = scene.InventoryService;
UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
item = invService.GetItem(item);
InventoryFolderBase folder = null;
if (item != null) // It's an item
{
item.Folder = destinationFolderID;
invService.DeleteItems(item.Owner, new List<UUID>() { item.ID });
scene.AddInventoryItem(client, item);
}
else
{
folder = new InventoryFolderBase(inventoryID, client.AgentId);
folder = invService.GetFolder(folder);
if (folder != null) // It's a folder
{
folder.ParentID = destinationFolderID;
invService.MoveFolder(folder);
}
}
}
}
else if (
im.dialog == (byte)InstantMessageDialog.InventoryDeclined
|| im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined)

View File

@@ -323,6 +323,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
// Validate assorted conditions
string reason = string.Empty;
if (!ValidateGenericConditions(sp, reg, finalDestination, teleportFlags, out reason))
{
sp.ControllingClient.SendTeleportFailed(reason);
return;
}
//
// This is it
//
@@ -354,6 +362,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
// Nothing to validate here
protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
{
reason = String.Empty;
return true;
}
/// <summary>
/// Determines whether this instance is within the max transfer distance.
/// </summary>
@@ -568,7 +583,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
if (!UpdateAgent(reg, finalDestination, agent))
if (!UpdateAgent(reg, finalDestination, agent, sp))
{
// Region doesn't take it
m_log.WarnFormat(
@@ -695,7 +710,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return success;
}
protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent)
protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent, ScenePresence sp)
{
return Scene.SimulationService.UpdateAgent(finalDestination, agent);
}

View File

@@ -54,6 +54,59 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
private GatekeeperServiceConnector m_GatekeeperConnector;
protected bool m_RestrictAppearanceAbroad;
protected string m_AccountName;
protected List<AvatarAppearance> m_ExportedAppearances;
protected List<AvatarAttachment> m_Attachs;
protected List<AvatarAppearance> ExportedAppearance
{
get
{
if (m_ExportedAppearances != null)
return m_ExportedAppearances;
m_ExportedAppearances = new List<AvatarAppearance>();
m_Attachs = new List<AvatarAttachment>();
string[] names = m_AccountName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string name in names)
{
string[] parts = name.Trim().Split();
if (parts.Length != 2)
{
m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Wrong user account name format {0}. Specify 'First Last'", name);
return null;
}
UserAccount account = Scene.UserAccountService.GetUserAccount(UUID.Zero, parts[0], parts[1]);
if (account == null)
{
m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unknown account {0}", m_AccountName);
return null;
}
AvatarAppearance a = Scene.AvatarService.GetAppearance(account.PrincipalID);
if (a != null)
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Successfully retrieved appearance for {0}", name);
foreach (AvatarAttachment att in a.GetAttachments())
{
InventoryItemBase item = new InventoryItemBase(att.ItemID, account.PrincipalID);
item = Scene.InventoryService.GetItem(item);
if (item != null)
a.SetAttachment(att.AttachPoint, att.ItemID, item.AssetID);
else
m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unable to retrieve item {0} from inventory {1}", att.ItemID, name);
}
m_ExportedAppearances.Add(a);
m_Attachs.AddRange(a.GetAttachments());
}
return m_ExportedAppearances;
}
}
#region ISharedRegionModule
public override string Name
@@ -72,8 +125,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
IConfig transferConfig = source.Configs["EntityTransfer"];
if (transferConfig != null)
{
m_levelHGTeleport = transferConfig.GetInt("LevelHGTeleport", 0);
m_RestrictAppearanceAbroad = transferConfig.GetBoolean("RestrictAppearanceAbroad", false);
if (m_RestrictAppearanceAbroad)
{
m_AccountName = transferConfig.GetString("AccountForAppearance", string.Empty);
if (m_AccountName == string.Empty)
m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is on, but no account has been given for avatar appearance!");
}
}
InitialiseCommon(source);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: {0} enabled.", Name);
}
@@ -85,7 +148,36 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
base.AddRegion(scene);
if (m_Enabled)
{
scene.RegisterModuleInterface<IUserAgentVerificationModule>(this);
scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
}
}
void OnIncomingSceneObject(SceneObjectGroup so)
{
if (!so.IsAttachment)
return;
if (so.Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar))
return;
// foreign user
AgentCircuitData aCircuit = so.Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar);
if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
{
if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
{
string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachement {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url);
Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(so.Scene.AssetService, url);
uuidGatherer.GatherAssetUuids(so, ids);
foreach (KeyValuePair<UUID, AssetType> kvp in ids)
uuidGatherer.FetchAsset(kvp.Key);
}
}
}
protected override void OnNewClient(IClientAPI client)
@@ -153,6 +245,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
// Log them out of this grid
Scene.PresenceService.LogoutAgent(sp.ControllingClient.SessionId);
string userId = Scene.UserManagementModule.GetUserUUI(sp.UUID);
Scene.GridUserService.LoggedOut(userId, UUID.Zero, Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
}
}
@@ -195,6 +289,124 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return base.CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout);
}
protected override bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
{
reason = "Please wear your grid's allowed appearance before teleporting to another grid";
if (!m_RestrictAppearanceAbroad)
return true;
// The rest is only needed for controlling appearance
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
{
// this user is going to another grid
if (Scene.UserManagementModule.IsLocalGridUser(sp.UUID))
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Checking generic appearance");
// Check wearables
for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
{
for (int j = 0; j < sp.Appearance.Wearables[i].Count; j++)
{
if (sp.Appearance.Wearables[i] == null)
continue;
bool found = false;
foreach (AvatarAppearance a in ExportedAppearance)
if (a.Wearables[i] != null)
{
found = true;
break;
}
if (!found)
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
return false;
}
found = false;
foreach (AvatarAppearance a in ExportedAppearance)
if (sp.Appearance.Wearables[i][j].AssetID == a.Wearables[i][j].AssetID)
{
found = true;
break;
}
if (!found)
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
return false;
}
}
}
// Check attachments
foreach (AvatarAttachment att in sp.Appearance.GetAttachments())
{
bool found = false;
foreach (AvatarAttachment att2 in m_Attachs)
{
if (att2.AssetID == att.AssetID)
{
found = true;
break;
}
}
if (!found)
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Attachment not allowed to go outside {0}", att.AttachPoint);
return false;
}
}
}
}
reason = string.Empty;
return true;
}
//protected override bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agentData, ScenePresence sp)
//{
// int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
// if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
// {
// // this user is going to another grid
// if (m_RestrictAppearanceAbroad && Scene.UserManagementModule.IsLocalGridUser(agentData.AgentID))
// {
// // We need to strip the agent off its appearance
// m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Sending generic appearance");
// // Delete existing npc attachments
// Scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false);
// // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet since it doesn't transfer attachments
// AvatarAppearance newAppearance = new AvatarAppearance(ExportedAppearance, true);
// sp.Appearance = newAppearance;
// // Rez needed npc attachments
// Scene.AttachmentsModule.RezAttachments(sp);
// IAvatarFactoryModule module = Scene.RequestModuleInterface<IAvatarFactoryModule>();
// //module.SendAppearance(sp.UUID);
// module.RequestRebake(sp, false);
// Scene.AttachmentsModule.CopyAttachments(sp, agentData);
// agentData.Appearance = sp.Appearance;
// }
// }
// foreach (AvatarAttachment a in agentData.Appearance.GetAttachments())
// m_log.DebugFormat("[XXX]: {0}-{1}", a.ItemID, a.AssetID);
// return base.UpdateAgent(reg, finalDestination, agentData, sp);
//}
public override void TeleportHome(UUID id, IClientAPI client)
{
m_log.DebugFormat(

View File

@@ -71,19 +71,19 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
#region Internal functions
public AssetBase FetchAsset(string url, UUID assetID)
public AssetMetadata FetchMetadata(string url, UUID assetID)
{
if (!url.EndsWith("/") && !url.EndsWith("="))
url = url + "/";
AssetBase asset = m_scene.AssetService.Get(url + assetID.ToString());
AssetMetadata meta = m_scene.AssetService.GetMetadata(url + assetID.ToString());
if (asset != null)
{
m_log.DebugFormat("[HG ASSET MAPPER]: Copied asset {0} from {1} to local asset server. ", asset.ID, url);
return asset;
}
return null;
if (meta != null)
m_log.DebugFormat("[HG ASSET MAPPER]: Fetched metadata for asset {0} of type {1} from {2} ", assetID, meta.Type, url);
else
m_log.DebugFormat("[HG ASSET MAPPER]: Unable to fetched metadata for asset {0} from {1} ", assetID, url);
return meta;
}
public bool PostAsset(string url, AssetBase asset)
@@ -93,6 +93,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
if (!url.EndsWith("/") && !url.EndsWith("="))
url = url + "/";
bool success = true;
// See long comment in AssetCache.AddAsset
if (!asset.Temporary || asset.Local)
{
@@ -103,14 +104,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
// not having a global naming infrastructure
AssetBase asset1 = new AssetBase(asset.FullID, asset.Name, asset.Type, asset.Metadata.CreatorID);
Copy(asset, asset1);
try
{
asset1.ID = url + asset.ID;
}
catch
{
m_log.Warn("[HG ASSET MAPPER]: Oops.");
}
asset1.ID = url + asset.ID;
AdjustIdentifiers(asset1.Metadata);
if (asset1.Metadata.Type == (sbyte)AssetType.Object)
@@ -118,11 +112,17 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
else
asset1.Data = asset.Data;
m_scene.AssetService.Store(asset1);
m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url);
string id = m_scene.AssetService.Store(asset1);
if (id == string.Empty)
{
m_log.DebugFormat("[HG ASSET MAPPER]: Asset server {0} did not accept {1}", url, asset.ID);
success = false;
}
else
m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url);
}
return true;
}
return success;
}
else
m_log.Warn("[HG ASSET MAPPER]: Tried to post asset to remote server, but asset not in local cache.");
@@ -222,28 +222,17 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
public void Get(UUID assetID, UUID ownerID, string userAssetURL)
{
// Get the item from the remote asset server onto the local AssetCache
// and place an entry in m_assetMap
// Get the item from the remote asset server onto the local AssetService
m_log.Debug("[HG ASSET MAPPER]: Fetching object " + assetID + " from asset server " + userAssetURL);
AssetBase asset = FetchAsset(userAssetURL, assetID);
AssetMetadata meta = FetchMetadata(userAssetURL, assetID);
if (meta == null)
return;
if (asset != null)
{
// OK, now fetch the inside.
Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
if (ids.ContainsKey(assetID))
ids.Remove(assetID);
foreach (UUID uuid in ids.Keys)
FetchAsset(userAssetURL, uuid);
// The act of gathering UUIDs downloads the assets from the remote server
Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL);
uuidGatherer.GatherAssetUuids(assetID, (AssetType)meta.Type, ids);
m_log.DebugFormat("[HG ASSET MAPPER]: Successfully fetched asset {0} from asset server {1}", asset.ID, userAssetURL);
}
else
m_log.Warn("[HG ASSET MAPPER]: Could not fetch asset from remote asset server " + userAssetURL);
}
@@ -257,19 +246,23 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
if (asset != null)
{
Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty);
HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, string.Empty);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
bool success = false;
foreach (UUID uuid in ids.Keys)
{
asset = m_scene.AssetService.Get(uuid.ToString());
if (asset == null)
m_log.DebugFormat("[HG ASSET MAPPER]: Could not find asset {0}", uuid);
else
PostAsset(userAssetURL, asset);
success = PostAsset(userAssetURL, asset);
}
// maybe all pieces got there...
m_log.DebugFormat("[HG ASSET MAPPER]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL);
// maybe all pieces got there...
if (!success)
m_log.DebugFormat("[HG ASSET MAPPER]: Problems posting item {0} to asset server {1}", assetID, userAssetURL);
else
m_log.DebugFormat("[HG ASSET MAPPER]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL);
}
else

View File

@@ -263,8 +263,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
//}
// OK, we're done fetching. Pass it up to the default RezObject
return base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection,
RezSelected, RemoveItem, fromTaskID, attachment);
SceneObjectGroup sog = base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection,
RezSelected, RemoveItem, fromTaskID, attachment);
if (sog == null)
remoteClient.SendAgentAlertMessage("Unable to rez: problem accessing inventory or locating assets", false);
return sog;
}
@@ -308,6 +313,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

@@ -1,161 +1,170 @@
/*
* 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.IO;
using System.Text;
using log4net;
namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging
{
/// <summary>
/// Class for writing a high performance, high volume log file.
/// Sometimes, to debug, one has a high volume logging to do and the regular
/// log file output is not appropriate.
/// Create a new instance with the parameters needed and
/// call Write() to output a line. Call Close() when finished.
/// If created with no parameters, it will not log anything.
/// </summary>
public class LogWriter : IDisposable
{
public bool Enabled { get; private set; }
private string m_logDirectory = ".";
private int m_logMaxFileTimeMin = 5; // 5 minutes
public String LogFileHeader { get; set; }
private StreamWriter m_logFile = null;
private TimeSpan m_logFileLife;
private DateTime m_logFileEndTime;
private Object m_logFileWriteLock = new Object();
// set externally when debugging. If let 'null', this does not write any error messages.
public ILog ErrorLogger = null;
private string LogHeader = "[LOG WRITER]";
/// <summary>
/// Create a log writer that will not write anything. Good for when not enabled
/// but the write statements are still in the code.
/// </summary>
public LogWriter()
{
Enabled = false;
m_logFile = null;
}
/// <summary>
/// Create a log writer instance.
/// </summary>
/// <param name="dir">The directory to create the log file in. May be 'null' for default.</param>
/// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param>
/// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param>
public LogWriter(string dir, string headr, int maxFileTime)
{
m_logDirectory = dir == null ? "." : dir;
LogFileHeader = headr == null ? "log-" : headr;
m_logMaxFileTimeMin = maxFileTime;
if (m_logMaxFileTimeMin < 1)
m_logMaxFileTimeMin = 5;
m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0);
m_logFileEndTime = DateTime.Now + m_logFileLife;
Enabled = true;
}
public void Dispose()
{
this.Close();
}
public void Close()
{
Enabled = false;
if (m_logFile != null)
{
m_logFile.Close();
m_logFile.Dispose();
m_logFile = null;
}
}
public void Write(string line, params object[] args)
{
if (!Enabled) return;
Write(String.Format(line, args));
}
public void Write(string line)
{
if (!Enabled) return;
try
{
lock (m_logFileWriteLock)
{
DateTime now = DateTime.Now;
if (m_logFile == null || now > m_logFileEndTime)
{
if (m_logFile != null)
{
m_logFile.Close();
m_logFile.Dispose();
m_logFile = null;
}
// First log file or time has expired, start writing to a new log file
m_logFileEndTime = now + m_logFileLife;
string path = (m_logDirectory.Length > 0 ? m_logDirectory
+ System.IO.Path.DirectorySeparatorChar.ToString() : "")
+ String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss"));
m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write));
}
if (m_logFile != null)
{
StringBuilder buff = new StringBuilder(line.Length + 25);
buff.Append(now.ToString("yyyyMMddHHmmssfff"));
// buff.Append(now.ToString("yyyyMMddHHmmss"));
buff.Append(",");
buff.Append(line);
buff.Append("\r\n");
m_logFile.Write(buff.ToString());
}
}
}
catch (Exception e)
{
if (ErrorLogger != null)
{
ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e);
}
Enabled = false;
}
return;
}
}
}
/*
* 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.IO;
using System.Text;
using log4net;
namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging
{
/// <summary>
/// Class for writing a high performance, high volume log file.
/// Sometimes, to debug, one has a high volume logging to do and the regular
/// log file output is not appropriate.
/// Create a new instance with the parameters needed and
/// call Write() to output a line. Call Close() when finished.
/// If created with no parameters, it will not log anything.
/// </summary>
public class LogWriter : IDisposable
{
public bool Enabled { get; private set; }
private string m_logDirectory = ".";
private int m_logMaxFileTimeMin = 5; // 5 minutes
public String LogFileHeader { get; set; }
private StreamWriter m_logFile = null;
private TimeSpan m_logFileLife;
private DateTime m_logFileEndTime;
private Object m_logFileWriteLock = new Object();
// set externally when debugging. If let 'null', this does not write any error messages.
public ILog ErrorLogger = null;
private string LogHeader = "[LOG WRITER]";
/// <summary>
/// Create a log writer that will not write anything. Good for when not enabled
/// but the write statements are still in the code.
/// </summary>
public LogWriter()
{
Enabled = false;
m_logFile = null;
}
/// <summary>
/// Create a log writer instance.
/// </summary>
/// <param name="dir">The directory to create the log file in. May be 'null' for default.</param>
/// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param>
/// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param>
public LogWriter(string dir, string headr, int maxFileTime)
{
m_logDirectory = dir == null ? "." : dir;
LogFileHeader = headr == null ? "log-" : headr;
m_logMaxFileTimeMin = maxFileTime;
if (m_logMaxFileTimeMin < 1)
m_logMaxFileTimeMin = 5;
m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0);
m_logFileEndTime = DateTime.Now + m_logFileLife;
Enabled = true;
}
public void Dispose()
{
this.Close();
}
public void Close()
{
Enabled = false;
if (m_logFile != null)
{
m_logFile.Close();
m_logFile.Dispose();
m_logFile = null;
}
}
public void Write(string line, params object[] args)
{
if (!Enabled) return;
Write(String.Format(line, args));
}
public void Flush()
{
if (!Enabled) return;
if (m_logFile != null)
{
m_logFile.Flush();
}
}
public void Write(string line)
{
if (!Enabled) return;
try
{
lock (m_logFileWriteLock)
{
DateTime now = DateTime.Now;
if (m_logFile == null || now > m_logFileEndTime)
{
if (m_logFile != null)
{
m_logFile.Close();
m_logFile.Dispose();
m_logFile = null;
}
// First log file or time has expired, start writing to a new log file
m_logFileEndTime = now + m_logFileLife;
string path = (m_logDirectory.Length > 0 ? m_logDirectory
+ System.IO.Path.DirectorySeparatorChar.ToString() : "")
+ String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss"));
m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write));
}
if (m_logFile != null)
{
StringBuilder buff = new StringBuilder(line.Length + 25);
buff.Append(now.ToString("yyyyMMddHHmmssfff"));
// buff.Append(now.ToString("yyyyMMddHHmmss"));
buff.Append(",");
buff.Append(line);
buff.Append("\r\n");
m_logFile.Write(buff.ToString());
}
}
}
catch (Exception e)
{
if (ErrorLogger != null)
{
ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e);
}
Enabled = false;
}
return;
}
}
}

View File

@@ -137,6 +137,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
ud.FirstName = words[0];
ud.LastName = "@" + words[1];
users.Add(ud);
// WARNING! that uriStr is not quite right... it may be missing the / at the end,
// which will cause trouble (duplicate entries on some tables). We should
// get the UUI instead from the UAS. TO BE FIXED.
AddUser(userID, names[0], names[1], uriStr);
m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} found", words[0], words[1]);
}

View File

@@ -429,8 +429,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
public void AddUser(UUID uuid, string first, string last, string homeURL)
{
// m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
//m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
AddUser(uuid, homeURL + ";" + first + " " + last);
}
@@ -553,8 +552,8 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
MainConsole.Instance.Output("-----------------------------------------------------------------------------");
foreach (KeyValuePair<UUID, UserData> kvp in m_UserCache)
{
MainConsole.Instance.Output(String.Format("{0} {1} {2}",
kvp.Key, kvp.Value.FirstName, kvp.Value.LastName));
MainConsole.Instance.Output(String.Format("{0} {1} {2} ({3})",
kvp.Key, kvp.Value.FirstName, kvp.Value.LastName, kvp.Value.HomeURL));
}
return;

View File

@@ -26,32 +26,36 @@
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class HGUuidGatherer : UuidGatherer
public class DynamicTexture : IDynamicTexture
{
protected string m_assetServerURL;
protected HGAssetMapper m_assetMapper;
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 HGUuidGatherer(HGAssetMapper assMap, IAssetService assetCache, string assetServerURL) : base(assetCache)
public DynamicTexture(string inputCommands, string inputParams, byte[] data, Size size, bool isReuseable)
{
m_assetMapper = assMap;
m_assetServerURL = assetServerURL;
InputCommands = inputCommands;
InputParams = inputParams;
Data = data;
Size = size;
IsReuseable = isReuseable;
}
protected override AssetBase GetAsset(UUID uuid)
public DynamicTexture(Uri inputUri, string inputParams, byte[] data, Size size, bool isReuseable)
{
if (string.Empty == m_assetServerURL)
return m_assetCache.Get(uuid.ToString());
else
return m_assetMapper.FetchAsset(m_assetServerURL, uuid);
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

@@ -211,6 +211,23 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
RegisterScriptInvocation(target, mi);
}
}
public void RegisterScriptInvocations(IRegionModuleBase target)
{
foreach(MethodInfo method in target.GetType().GetMethods(
BindingFlags.Public | BindingFlags.Instance |
BindingFlags.Static))
{
if(method.GetCustomAttributes(
typeof(ScriptInvocationAttribute), true).Any())
{
if(method.IsStatic)
RegisterScriptInvocation(target.GetType(), method);
else
RegisterScriptInvocation(target, method);
}
}
}
public Delegate[] GetScriptInvocationList()
{
@@ -313,6 +330,20 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
}
}
public void RegisterConstants(IRegionModuleBase target)
{
foreach (FieldInfo field in target.GetType().GetFields(
BindingFlags.Public | BindingFlags.Static |
BindingFlags.Instance))
{
if (field.GetCustomAttributes(
typeof(ScriptConstantAttribute), true).Any())
{
RegisterConstant(field.Name, field.GetValue(target));
}
}
}
/// <summary>
/// Operation to check for a registered constant
/// </summary>

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;
}
@@ -115,14 +112,19 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public void GetDrawStringSize(string text, string fontName, int fontSize,
out double xSize, out double ySize)
{
using (Font myFont = new Font(fontName, fontSize))
lock (this)
{
SizeF stringSize = new SizeF();
lock (m_graph)
using (Font myFont = new Font(fontName, fontSize))
{
stringSize = m_graph.MeasureString(text, myFont);
xSize = stringSize.Width;
ySize = stringSize.Height;
SizeF stringSize = new SizeF();
// XXX: This lock may be unnecessary.
lock (m_graph)
{
stringSize = m_graph.MeasureString(text, myFont);
xSize = stringSize.Width;
ySize = stringSize.Height;
}
}
}
}
@@ -161,6 +163,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 +188,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 +331,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
Bitmap bitmap = null;
Graphics graph = null;
bool reuseable = false;
try
{
@@ -364,6 +374,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 +394,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

@@ -56,6 +56,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
private bool m_Enabled = false;
private AssetPermissions m_AssetPerms;
public Type ReplaceableInterface
{
get { return null; }
@@ -128,6 +130,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
if (m_LocalAssetServiceURI != string.Empty)
m_LocalAssetServiceURI = m_LocalAssetServiceURI.Trim('/');
IConfig hgConfig = source.Configs["HGAssetService"];
m_AssetPerms = new AssetPermissions(hgConfig); // it's ok if arg is null
m_Enabled = true;
m_log.Info("[HG ASSET CONNECTOR]: HG asset broker enabled");
}
@@ -206,14 +211,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
asset = m_HGService.Get(id);
if (asset != null)
{
// Now store it locally
// For now, let me just do it for textures and scripts
if (((AssetType)asset.Type == AssetType.Texture) ||
((AssetType)asset.Type == AssetType.LSLBytecode) ||
((AssetType)asset.Type == AssetType.LSLText))
{
// Now store it locally, if allowed
if (m_AssetPerms.AllowedImport(asset.Type))
m_GridService.Store(asset);
}
else
return null;
}
}
else
@@ -328,7 +330,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
string id = string.Empty;
if (IsHG(asset.ID))
id = m_HGService.Store(asset);
{
if (m_AssetPerms.AllowedExport(asset.Type))
id = m_HGService.Store(asset);
else
return String.Empty;
}
else
id = m_GridService.Store(asset);

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,11 +43,15 @@ 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()
[SetUp]
public override void SetUp()
{
base.SetUp();
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.AddConfig("GridService");
@@ -71,8 +75,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
SetUp();
// Create 4 regions
GridRegion r1 = new GridRegion();
r1.RegionName = "Test Region 1";

View File

@@ -65,11 +65,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
public void OnMakeRootAgent(ScenePresence sp)
{
// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName);
if (sp.PresenceType != PresenceType.Npc)
{
string userid = sp.Scene.UserManagementModule.GetUserUUI(sp.UUID);
//m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", userid, sp.Scene.RegionInfo.RegionName);
m_GridUserService.SetLastPosition(
sp.UUID.ToString(), UUID.Zero, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
userid, UUID.Zero, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
}
}
public void OnNewClient(IClientAPI client)
@@ -82,9 +84,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
if (client.SceneAgent.IsChildAgent)
return;
// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName);
string userId = client.AgentId.ToString();
if (client.Scene is Scene)
{
Scene s = (Scene)client.Scene;
userId = s.UserManagementModule.GetUserUUI(client.AgentId);
}
//m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", userId, client.Scene.RegionInfo.RegionName);
m_GridUserService.LoggedOut(
client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID,
userId, client.SessionId, client.Scene.RegionInfo.RegionID,
client.SceneAgent.AbsolutePosition, client.SceneAgent.Lookat);
}
}

View File

@@ -44,6 +44,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int KEEPTIME = 30; // 30 secs
private ExpiringCache<string, GridUserInfo> m_Infos = new ExpiringCache<string, GridUserInfo>();
#region ISharedRegionModule
private bool m_Enabled = false;
@@ -128,23 +131,60 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
public bool LoggedOut(string userID, UUID sessionID, UUID region, Vector3 position, Vector3 lookat)
{
if (m_Infos.Contains(userID))
m_Infos.Remove(userID);
return m_RemoteConnector.LoggedOut(userID, sessionID, region, position, lookat);
}
public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt)
{
return m_RemoteConnector.SetHome(userID, regionID, position, lookAt);
if (m_RemoteConnector.SetHome(userID, regionID, position, lookAt))
{
// Update the cache too
GridUserInfo info = null;
if (m_Infos.TryGetValue(userID, out info))
{
info.HomeRegionID = regionID;
info.HomePosition = position;
info.HomeLookAt = lookAt;
}
return true;
}
return false;
}
public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt)
{
return m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt);
if (m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt))
{
// Update the cache too
GridUserInfo info = null;
if (m_Infos.TryGetValue(userID, out info))
{
info.LastRegionID = regionID;
info.LastPosition = position;
info.LastLookAt = lookAt;
}
return true;
}
return false;
}
public GridUserInfo GetGridUserInfo(string userID)
{
return m_RemoteConnector.GetGridUserInfo(userID);
GridUserInfo info = null;
if (m_Infos.TryGetValue(userID, out info))
return info;
info = m_RemoteConnector.GetGridUserInfo(userID);
m_Infos.AddOrUpdate(userID, info, KEEPTIME);
return info;
}
public GridUserInfo[] GetGridUserInfo(string[] userID)

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,41 @@ 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()
public override void SetUp()
{
base.SetUp();
// FIXME: Do something about this - relying on statics in unit tests causes trouble sooner or later
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 +137,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 +179,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 +204,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 +287,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 +324,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 +379,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 +405,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 +432,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 +479,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 +538,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 +577,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 +687,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 +723,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

@@ -108,6 +108,15 @@ namespace OpenSim.Region.Framework.Interfaces
/// <param name="objectLocalID"></param>
void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID);
/// <summary>
/// Detach the given item to the ground at the specified coordinates & rotation
/// </summary>
/// <param name="sp"></param>
/// <param name="objectLocalID"></param>
/// <param name="absolutePos"></param>
/// <param name="absoluteRot"></param>
void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID, Vector3 absolutePos, Quaternion absoluteRot);
/// <summary>
/// Detach the given attachment so that it remains in the user's inventory.
/// </summary>

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

@@ -74,6 +74,14 @@ namespace OpenSim.Region.Framework.Interfaces
/// <param name="methods"></param>
void RegisterScriptInvocation(Type target, string[] methods);
/// <summary>
/// Automatically register script invocations by checking for methods
/// with <see cref="ScriptInvocationAttribute"/>. Should only check
/// public methods.
/// </summary>
/// <param name="target"></param>
void RegisterScriptInvocations(IRegionModuleBase target);
/// <summary>
/// Returns an array of all registered script calls
/// </summary>
@@ -96,11 +104,43 @@ namespace OpenSim.Region.Framework.Interfaces
/// <param name="key"></param>
void DispatchReply(UUID scriptId, int code, string text, string key);
/// For constants
/// <summary>
/// Operation to for a region module to register a constant to be used
/// by the script engine
/// </summary>
/// <param name="cname">
/// The name of the constant. LSL convention is for constant names to
/// be uppercase.
/// </param>
/// <param name="value">
/// The value of the constant. Should be of a type that can be
/// converted to one of <see cref="OpenSim.Region.ScriptEngine.Shared.LSL_Types"/>
/// </param>
void RegisterConstant(string cname, object value);
/// <summary>
/// Automatically register all constants on a region module by
/// checking for fields with <see cref="ScriptConstantAttribute"/>.
/// </summary>
/// <param name="target"></param>
void RegisterConstants(IRegionModuleBase target);
/// <summary>
/// Operation to check for a registered constant
/// </summary>
/// <param name="cname">Name of constant</param>
/// <returns>Value of constant or null if none found.</returns>
object LookupModConstant(string cname);
// For use ONLY by the script API
void RaiseEvent(UUID script, string id, string module, string command, string key);
}
[AttributeUsage(AttributeTargets.Method)]
public class ScriptInvocationAttribute : Attribute
{ }
[AttributeUsage(AttributeTargets.Field)]
public class ScriptConstantAttribute : Attribute
{ }
}

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,22 +132,74 @@ 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);
/// <summary>
/// Triggered after <see cref="OpenSim.IApplicationPlugin.PostInitialise"/>
/// has been called for all <see cref="OpenSim.IApplicationPlugin"/>
/// loaded via <see cref="OpenSim.OpenSimBase.LoadPlugins"/>.
/// Handlers for this event are typically used to parse the arguments
/// from <see cref="OnPluginConsoleDelegate"/> in order to process or
/// filter the arguments and pass them onto <see cref="OpenSim.Region.CoreModules.Framework.InterfaceCommander.Commander.ProcessConsoleCommand"/>
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerOnPluginConsole"/> in
/// <see cref="Scene.SendCommandToPlugins"/> via
/// <see cref="SceneManager.SendCommandToPluginModules"/> via
/// <see cref="OpenSim.OpenSimBase.HandleCommanderCommand"/> via
/// <see cref="OpenSim.OpenSimBase.AddPluginCommands"/> via
/// <see cref="OpenSim.OpenSimBase.StartupSpecific"/>
/// </remarks>
public event OnPluginConsoleDelegate OnPluginConsole;
/// <summary>
@@ -117,8 +214,28 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void OnSetRootAgentSceneDelegate(UUID agentID, Scene scene);
/// <summary>
/// Triggered before the grunt work for adding a root agent to a
/// scene has been performed (resuming attachment scripts, physics,
/// animations etc.)
/// </summary>
/// <remarks>
/// Triggered before <see cref="OnMakeRootAgent"/>
/// by <see cref="TriggerSetRootAgentScene"/>
/// in <see cref="ScenePresence.MakeRootAgent"/>
/// via <see cref="Scene.AgentCrossing"/>
/// and <see cref="ScenePresence.CompleteMovement"/>
/// </remarks>
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>
@@ -133,13 +250,45 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Fired when an object is touched/grabbed.
/// </summary>
/// <remarks>
/// The originalID is the local ID of the part that was actually touched. The localID itself is always that of
/// the root part.
/// Triggerd in response to <see cref="OpenSim.Framework.IClientAPI.OnGrabObject"/>
/// via <see cref="TriggerObjectGrab"/>
/// in <see cref="Scene.ProcessObjectGrab"/>
/// </remarks>
public event ObjectGrabDelegate OnObjectGrab;
public delegate void ObjectGrabDelegate(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs);
/// <summary>
/// Triggered when an object is being touched/grabbed continuously.
/// </summary>
/// <remarks>
/// Triggered in response to <see cref="OpenSim.Framework.IClientAPI.OnGrabUpdate"/>
/// via <see cref="TriggerObjectGrabbing"/>
/// in <see cref="Scene.ProcessObjectGrabUpdate"/>
/// </remarks>
public event ObjectGrabDelegate OnObjectGrabbing;
/// <summary>
/// Triggered when an object stops being touched/grabbed.
/// </summary>
/// <remarks>
/// Triggered in response to <see cref="OpenSim.Framework.IClientAPI.OnDeGrabObject"/>
/// via <see cref="TriggerObjectDeGrab"/>
/// in <see cref="Scene.ProcessObjectDeGrab"/>
/// </remarks>
public event ObjectDeGrabDelegate OnObjectDeGrab;
/// <summary>
/// Triggered when a script resets.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptReset"/>
/// in <see cref="Scene.ProcessScriptReset"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnScriptReset"/>
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleScriptReset"/>
/// </remarks>
public event ScriptResetDelegate OnScriptReset;
public event OnPermissionErrorDelegate OnPermissionError;
@@ -149,29 +298,105 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
/// <remarks>
/// Occurs after OnNewScript.
/// Triggered by <see cref="TriggerRezScript"/>
/// in <see cref="SceneObjectPartInventory.CreateScriptInstance"/>
/// </remarks>
public event NewRezScript OnRezScript;
public delegate void NewRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource);
public delegate void RemoveScript(uint localID, UUID itemID);
/// <summary>
/// Triggered when a script is removed from an object.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerRemoveScript"/>
/// in <see cref="Scene.RemoveTaskInventory"/>,
/// <see cref="Scene.CreateAgentInventoryItemFromTask"/>,
/// <see cref="SceneObjectPartInventory.RemoveScriptInstance"/>,
/// <see cref="SceneObjectPartInventory.RemoveInventoryItem"/>
/// </remarks>
public event RemoveScript OnRemoveScript;
public delegate void StartScript(uint localID, UUID itemID);
/// <summary>
/// Triggered when a script starts.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerStartScript"/>
/// in <see cref="Scene.SetScriptRunning"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnSetScriptRunning"/>,
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.HandleSetScriptRunning"/>
/// </remarks>
public event StartScript OnStartScript;
public delegate void StopScript(uint localID, UUID itemID);
/// <summary>
/// Triggered when a script stops.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerStopScript"/>,
/// in <see cref="SceneObjectPartInventory.CreateScriptInstance"/>,
/// <see cref="SceneObjectPartInventory.StopScriptInstance"/>,
/// <see cref="Scene.SetScriptRunning"/>
/// </remarks>
public event StopScript OnStopScript;
public delegate bool SceneGroupMoved(UUID groupID, Vector3 delta);
/// <summary>
/// Triggered when an object is moved.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerGroupMove"/>
/// in <see cref="SceneObjectGroup.UpdateGroupPosition"/>,
/// <see cref="SceneObjectGroup.GrabMovement"/>
/// </remarks>
public event SceneGroupMoved OnSceneGroupMove;
public delegate void SceneGroupGrabed(UUID groupID, Vector3 offset, UUID userID);
/// <summary>
/// Triggered when an object is grabbed.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerGroupGrab"/>
/// in <see cref="SceneObjectGroup.OnGrabGroup"/>
/// via <see cref="SceneObjectGroup.ObjectGrabHandler"/>
/// via <see cref="Scene.ProcessObjectGrab"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnGrabObject"/>
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectGrab"/>
/// </remarks>
public event SceneGroupGrabed OnSceneGroupGrab;
public delegate bool SceneGroupSpinStarted(UUID groupID);
/// <summary>
/// Triggered when an object starts to spin.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerGroupSpinStart"/>
/// in <see cref="SceneObjectGroup.SpinStart"/>
/// via <see cref="SceneGraph.SpinStart"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnSpinStart"/>
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectSpinStart"/>
/// </remarks>
public event SceneGroupSpinStarted OnSceneGroupSpinStart;
public delegate bool SceneGroupSpun(UUID groupID, Quaternion rotation);
/// <summary>
/// Triggered when an object is being spun.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerGroupSpin"/>
/// in <see cref="SceneObjectGroup.SpinMovement"/>
/// via <see cref="SceneGraph.SpinObject"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnSpinUpdate"/>
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectSpinUpdate"/>
/// </remarks>
public event SceneGroupSpun OnSceneGroupSpin;
public delegate void LandObjectAdded(ILandObject newParcel);
@@ -210,6 +435,9 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
/// <remarks>
/// Occurs before OnRezScript
/// Triggered by <see cref="TriggerNewScript"/>
/// in <see cref="Scene.RezScriptFromAgentInventory"/>,
/// <see cref="Scene.RezNewScript"/>
/// </remarks>
public event NewScript OnNewScript;
@@ -244,6 +472,12 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
/// <remarks>
/// Triggered after the scene receives a client's upload of an updated script and has stored it in an asset.
/// Triggered by <see cref="TriggerUpdateScript"/>
/// in <see cref="Scene.CapsUpdateTaskInventoryScriptAsset"/>
/// via <see cref="Scene.CapsUpdateTaskInventoryScriptAsset"/>
/// via <see cref="OpenSim.Region.ClientStack.Linden.BunchOfCaps.TaskScriptUpdated"/>
/// via <see cref="OpenSim.Region.ClientStack.Linden.TaskInventoryScriptUpdater.OnUpLoad"/>
/// via <see cref="OpenSim.Region.ClientStack.Linden.TaskInventoryScriptUpdater.uploaderCaps"/>
/// </remarks>
public event UpdateScript OnUpdateScript;
@@ -269,48 +503,203 @@ namespace OpenSim.Region.Framework.Scenes
}
/// <summary>
/// Triggered when some scene object properties change.
/// </summary>
/// <remarks>
/// ScriptChangedEvent is fired when a scene object property that a script might be interested
/// in (such as color, scale or inventory) changes. Only enough information sent is for the LSL changed event.
/// This is not an indication that the script has changed (see OnUpdateScript for that).
/// This event is sent to a script to tell it that some property changed on
/// the object the script is in. See http://lslwiki.net/lslwiki/wakka.php?wakka=changed .
/// </summary>
/// Triggered by <see cref="TriggerOnScriptChangedEvent"/>
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.TeleportAgentWithinRegion"/>,
/// <see cref="SceneObjectPart.TriggerScriptChangedEvent"/>
/// </remarks>
public event ScriptChangedEvent OnScriptChangedEvent;
public delegate void ScriptChangedEvent(uint localID, uint change);
public delegate void ScriptControlEvent(UUID item, UUID avatarID, uint held, uint changed);
/// <summary>
/// Triggered when a script receives control input from an agent.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerControlEvent"/>
/// in <see cref="ScenePresence.SendControlsToScripts"/>
/// via <see cref="ScenePresence.HandleAgentUpdate"/>
/// via <see cref="OpenSim.Framework.IClientAPI.OnAgentUpdate"/>
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleAgentUpdate"/>
/// </remarks>
public event ScriptControlEvent OnScriptControlEvent;
public delegate void ScriptAtTargetEvent(uint localID, uint handle, Vector3 targetpos, Vector3 atpos);
/// <summary>
/// Triggered when an object has arrived within a tolerance distance
/// of a motion target.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerAtTargetEvent"/>
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
/// </remarks>
public event ScriptAtTargetEvent OnScriptAtTargetEvent;
public delegate void ScriptNotAtTargetEvent(uint localID);
/// <summary>
/// Triggered when an object has a motion target but has not arrived
/// within a tolerance distance.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerNotAtTargetEvent"/>
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
/// </remarks>
public event ScriptNotAtTargetEvent OnScriptNotAtTargetEvent;
public delegate void ScriptAtRotTargetEvent(uint localID, uint handle, Quaternion targetrot, Quaternion atrot);
/// <summary>
/// Triggered when an object has arrived within a tolerance rotation
/// of a rotation target.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerAtRotTargetEvent"/>
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
/// </remarks>
public event ScriptAtRotTargetEvent OnScriptAtRotTargetEvent;
public delegate void ScriptNotAtRotTargetEvent(uint localID);
/// <summary>
/// Triggered when an object has a rotation target but has not arrived
/// within a tolerance rotation.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerNotAtRotTargetEvent"/>
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
/// </remarks>
public event ScriptNotAtRotTargetEvent OnScriptNotAtRotTargetEvent;
public delegate void ScriptColliding(uint localID, ColliderArgs colliders);
/// <summary>
/// Triggered when a physical collision has started between a prim
/// and something other than the region terrain.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptCollidingStart"/>
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptColliderStart;
/// <summary>
/// Triggered when something that previously collided with a prim has
/// not stopped colliding with it.
/// </summary>
/// <remarks>
/// <seealso cref="OnScriptColliderStart"/>
/// Triggered by <see cref="TriggerScriptColliding"/>
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptColliding;
/// <summary>
/// Triggered when something that previously collided with a prim has
/// stopped colliding with it.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptCollidingEnd"/>
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptCollidingEnd;
/// <summary>
/// Triggered when a physical collision has started between an object
/// and the region terrain.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptLandCollidingStart"/>
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptLandColliderStart;
/// <summary>
/// Triggered when an object that previously collided with the region
/// terrain has not yet stopped colliding with it.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptLandColliding"/>
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptLandColliding;
/// <summary>
/// Triggered when an object that previously collided with the region
/// terrain has stopped colliding with it.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerScriptLandCollidingEnd"/>
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
/// </remarks>
public event ScriptColliding OnScriptLandColliderEnd;
public delegate void OnMakeChildAgentDelegate(ScenePresence presence);
/// <summary>
/// Triggered when an agent has been made a child agent of a scene.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerOnMakeChildAgent"/>
/// in <see cref="ScenePresence.MakeChildAgent"/>
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.CrossAgentToNewRegionAsync"/>,
/// <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>,
/// <see cref="OpenSim.Region.CoreModules.InterGrid.KillAUser.ShutdownNoLogout"/>
/// </remarks>
public event OnMakeChildAgentDelegate OnMakeChildAgent;
public delegate void OnSaveNewWindlightProfileDelegate();
public delegate void OnSendNewWindlightProfileTargetedDelegate(RegionLightShareData wl, UUID user);
/// <summary>
/// Triggered after the grunt work for adding a root agent to a
/// scene has been performed (resuming attachment scripts, physics,
/// animations etc.)
/// </summary>
/// <remarks>
/// This event is on the critical path for transferring an avatar from one region to another. Try and do
/// as little work on this event as possible, or do work asynchronously.
/// </summary>
/// Triggered after <see cref="OnSetRootAgentScene"/>
/// by <see cref="TriggerOnMakeRootAgent"/>
/// in <see cref="ScenePresence.MakeRootAgent"/>
/// via <see cref="Scene.AgentCrossing"/>
/// and <see cref="ScenePresence.CompleteMovement"/>
/// </remarks>
public event Action<ScenePresence> OnMakeRootAgent;
public event OnSendNewWindlightProfileTargetedDelegate OnSendNewWindlightProfileTargeted;
@@ -336,9 +725,17 @@ namespace OpenSim.Region.Framework.Scenes
public event AvatarKillData OnAvatarKilled;
public delegate void AvatarKillData(uint KillerLocalID, ScenePresence avatar);
// public delegate void ScriptTimerEvent(uint localID, double timerinterval);
// public event ScriptTimerEvent OnScriptTimerEvent;
/*
public delegate void ScriptTimerEvent(uint localID, double timerinterval);
/// <summary>
/// Used to be triggered when the LSL timer event fires.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerTimerEvent"/>
/// via <see cref="SceneObjectPart.handleTimerAccounting"/>
/// </remarks>
public event ScriptTimerEvent OnScriptTimerEvent;
*/
public delegate void EstateToolsSunUpdate(ulong regionHandle, bool FixedTime, bool EstateSun, float LindenHour);
public delegate void GetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID);
@@ -348,12 +745,27 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Triggered when an object is added to the scene.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerObjectAddedToScene"/>
/// in <see cref="Scene.AddNewSceneObject"/>,
/// <see cref="Scene.DuplicateObject"/>,
/// <see cref="Scene.doObjectDuplicateOnRay"/>
/// </remarks>
public event Action<SceneObjectGroup> OnObjectAddedToScene;
/// <summary>
/// Delegate for <see cref="OnObjectBeingRemovedFromScene"/>
/// </summary>
/// <param name="obj">The object being removed from the scene</param>
public delegate void ObjectBeingRemovedFromScene(SceneObjectGroup obj);
/// <summary>
/// Triggered when an object is removed from the scene.
/// </summary>
public delegate void ObjectBeingRemovedFromScene(SceneObjectGroup obj);
/// <remarks>
/// Triggered by <see cref="TriggerObjectBeingRemovedFromScene"/>
/// in <see cref="Scene.DeleteSceneObject"/>
/// </remarks>
public event ObjectBeingRemovedFromScene OnObjectBeingRemovedFromScene;
public delegate void NoticeNoLandDataFromStorage();
@@ -369,6 +781,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 +854,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 +907,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);
@@ -522,9 +951,28 @@ namespace OpenSim.Region.Framework.Scenes
public event PrimsLoaded OnPrimsLoaded;
public delegate void TeleportStart(IClientAPI client, GridRegion destination, GridRegion finalDestination, uint teleportFlags, bool gridLogout);
/// <summary>
/// Triggered when a teleport starts
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerTeleportStart"/>
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.CreateAgent"/>
/// and <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.HGEntityTransferModule.CreateAgent"/>
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>
/// </remarks>
public event TeleportStart OnTeleportStart;
public delegate void TeleportFail(IClientAPI client, bool gridLogout);
/// <summary>
/// Trigered when a teleport fails.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerTeleportFail"/>
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.Fail"/>
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>
/// </remarks>
public event TeleportFail OnTeleportFail;
public class MoneyTransferArgs : EventArgs
@@ -532,7 +980,9 @@ namespace OpenSim.Region.Framework.Scenes
public UUID sender;
public UUID receiver;
// Always false. The SL protocol sucks.
/// <summary>
/// Always false. The SL protocol sucks.
/// </summary>
public bool authenticated = false;
public int amount;
@@ -589,8 +1039,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)
@@ -2006,7 +2477,11 @@ namespace OpenSim.Region.Framework.Scenes
}
}
// this lets us keep track of nasty script events like timer, etc.
/// <summary>
/// this lets us keep track of nasty script events like timer, etc.
/// </summary>
/// <param name="objLocalID"></param>
/// <param name="Interval"></param>
public void TriggerTimerEvent(uint objLocalID, double Interval)
{
throw new NotImplementedException("TriggerTimerEvent was thought to be not used anymore and the registration for the event from scene object part has been commented out due to a memory leak");
@@ -2068,7 +2543,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 +2552,7 @@ namespace OpenSim.Region.Framework.Scenes
{
try
{
d(requestId, message);
d(requestId, loadedScenes, message);
}
catch (Exception e)
{

View File

@@ -106,7 +106,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Minimum value of the size of a non-physical prim in each axis
/// </summary>
public float m_minNonphys = 0.01f;
public float m_minNonphys = 0.001f;
/// <summary>
/// Maximum value of the size of a non-physical prim in each axis
@@ -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;
@@ -146,7 +151,8 @@ namespace OpenSim.Region.Framework.Scenes
// TODO: need to figure out how allow client agents but deny
// root agents when ACL denies access to root agent
public bool m_strictAccessControl = true;
public int MaxUndoCount = 5;
public int MaxUndoCount { get; set; }
// Using this for RegionReady module to prevent LoginsDisabled from changing under our feet;
public bool LoginLock = false;
@@ -736,16 +742,18 @@ namespace OpenSim.Region.Framework.Scenes
//Animation states
m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false);
MaxUndoCount = startupConfig.GetInt("MaxPrimUndos", 20);
PhysicalPrims = startupConfig.GetBoolean("physical_prim", PhysicalPrims);
CollidablePrims = startupConfig.GetBoolean("collidable_prim", CollidablePrims);
m_minNonphys = startupConfig.GetFloat("NonphysicalPrimMin", m_minNonphys);
m_minNonphys = startupConfig.GetFloat("NonPhysicalPrimMin", m_minNonphys);
if (RegionInfo.NonphysPrimMin > 0)
{
m_minNonphys = RegionInfo.NonphysPrimMin;
}
m_maxNonphys = startupConfig.GetFloat("NonphysicalPrimMax", m_maxNonphys);
m_maxNonphys = startupConfig.GetFloat("NonPhysicalPrimMax", m_maxNonphys);
if (RegionInfo.NonphysPrimMax > 0)
{
m_maxNonphys = RegionInfo.NonphysPrimMax;
@@ -772,6 +780,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);
@@ -850,6 +864,8 @@ namespace OpenSim.Region.Framework.Scenes
}
// FIXME: Ultimately this should be in a module.
SendPeriodicAppearanceUpdates = true;
IConfig appearanceConfig = m_config.Configs["Appearance"];
if (appearanceConfig != null)
{
@@ -4334,6 +4350,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 +4607,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 +4650,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>
@@ -5482,6 +5532,9 @@ namespace OpenSim.Region.Framework.Scenes
public string GetExtraSetting(string name)
{
if (m_extraSettings == null)
return String.Empty;
string val;
if (!m_extraSettings.TryGetValue(name, out val))
@@ -5492,6 +5545,9 @@ namespace OpenSim.Region.Framework.Scenes
public void StoreExtraSetting(string name, string val)
{
if (m_extraSettings == null)
return;
string oldVal;
if (m_extraSettings.TryGetValue(name, out oldVal))
@@ -5509,6 +5565,9 @@ namespace OpenSim.Region.Framework.Scenes
public void RemoveExtraSetting(string name)
{
if (m_extraSettings == null)
return;
if (!m_extraSettings.ContainsKey(name))
return;

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

@@ -1119,14 +1119,6 @@ namespace OpenSim.Region.Framework.Scenes
parts[i].UUID = UUID.Random();
}
// helper provided for parts.
public int GetSceneMaxUndo()
{
if (m_scene != null)
return m_scene.MaxUndoCount;
return 5;
}
// justincc: I don't believe this hack is needed any longer, especially since the physics
// parts of set AbsolutePosition were already commented out. By changing HasGroupChanged to false
// this method was preventing proper reload of scene objects.
@@ -2014,6 +2006,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;
@@ -2684,29 +2695,32 @@ namespace OpenSim.Region.Framework.Scenes
// m_log.DebugFormat(
// "[SCENE OBJECT GROUP]: Group resizing {0} {1} from {2} to {3}", Name, LocalId, RootPart.Scale, scale);
RootPart.StoreUndoState(true);
scale.X = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.X));
scale.Y = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Y));
scale.Z = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Z));
PhysicsActor pa = m_rootPart.PhysActor;
if (pa != null && pa.IsPhysical)
RootPart.StoreUndoState(true);
if (Scene != null)
{
scale.X = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.X));
scale.Y = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Y));
scale.Z = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Z));
scale.X = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.X));
scale.Y = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Y));
scale.Z = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Z));
if (pa != null && pa.IsPhysical)
{
scale.X = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.X));
scale.Y = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Y));
scale.Z = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Z));
}
}
float x = (scale.X / RootPart.Scale.X);
float y = (scale.Y / RootPart.Scale.Y);
float z = (scale.Z / RootPart.Scale.Z);
SceneObjectPart[] parts;
if (x > 1.0f || y > 1.0f || z > 1.0f)
SceneObjectPart[] parts = m_parts.GetArray();
if (Scene != null & (x > 1.0f || y > 1.0f || z > 1.0f))
{
parts = m_parts.GetArray();
for (int i = 0; i < parts.Length; i++)
{
SceneObjectPart obPart = parts[i];
@@ -2720,7 +2734,7 @@ namespace OpenSim.Region.Framework.Scenes
if (pa != null && pa.IsPhysical)
{
if (oldSize.X * x > m_scene.m_maxPhys)
if (oldSize.X * x > Scene.m_maxPhys)
{
f = m_scene.m_maxPhys / oldSize.X;
a = f / x;
@@ -2728,7 +2742,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.X * x < m_scene.m_minPhys)
else if (oldSize.X * x < Scene.m_minPhys)
{
f = m_scene.m_minPhys / oldSize.X;
a = f / x;
@@ -2737,7 +2751,7 @@ namespace OpenSim.Region.Framework.Scenes
z *= a;
}
if (oldSize.Y * y > m_scene.m_maxPhys)
if (oldSize.Y * y > Scene.m_maxPhys)
{
f = m_scene.m_maxPhys / oldSize.Y;
a = f / y;
@@ -2745,7 +2759,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.Y * y < m_scene.m_minPhys)
else if (oldSize.Y * y < Scene.m_minPhys)
{
f = m_scene.m_minPhys / oldSize.Y;
a = f / y;
@@ -2754,7 +2768,7 @@ namespace OpenSim.Region.Framework.Scenes
z *= a;
}
if (oldSize.Z * z > m_scene.m_maxPhys)
if (oldSize.Z * z > Scene.m_maxPhys)
{
f = m_scene.m_maxPhys / oldSize.Z;
a = f / z;
@@ -2762,7 +2776,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.Z * z < m_scene.m_minPhys)
else if (oldSize.Z * z < Scene.m_minPhys)
{
f = m_scene.m_minPhys / oldSize.Z;
a = f / z;
@@ -2773,7 +2787,7 @@ namespace OpenSim.Region.Framework.Scenes
}
else
{
if (oldSize.X * x > m_scene.m_maxNonphys)
if (oldSize.X * x > Scene.m_maxNonphys)
{
f = m_scene.m_maxNonphys / oldSize.X;
a = f / x;
@@ -2781,7 +2795,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.X * x < m_scene.m_minNonphys)
else if (oldSize.X * x < Scene.m_minNonphys)
{
f = m_scene.m_minNonphys / oldSize.X;
a = f / x;
@@ -2790,7 +2804,7 @@ namespace OpenSim.Region.Framework.Scenes
z *= a;
}
if (oldSize.Y * y > m_scene.m_maxNonphys)
if (oldSize.Y * y > Scene.m_maxNonphys)
{
f = m_scene.m_maxNonphys / oldSize.Y;
a = f / y;
@@ -2798,7 +2812,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.Y * y < m_scene.m_minNonphys)
else if (oldSize.Y * y < Scene.m_minNonphys)
{
f = m_scene.m_minNonphys / oldSize.Y;
a = f / y;
@@ -2807,7 +2821,7 @@ namespace OpenSim.Region.Framework.Scenes
z *= a;
}
if (oldSize.Z * z > m_scene.m_maxNonphys)
if (oldSize.Z * z > Scene.m_maxNonphys)
{
f = m_scene.m_maxNonphys / oldSize.Z;
a = f / z;
@@ -2815,7 +2829,7 @@ namespace OpenSim.Region.Framework.Scenes
y *= a;
z *= a;
}
else if (oldSize.Z * z < m_scene.m_minNonphys)
else if (oldSize.Z * z < Scene.m_minNonphys)
{
f = m_scene.m_minNonphys / oldSize.Z;
a = f / z;
@@ -2839,7 +2853,6 @@ namespace OpenSim.Region.Framework.Scenes
RootPart.Resize(prevScale);
// RootPart.IgnoreUndoUpdate = false;
parts = m_parts.GetArray();
for (int i = 0; i < parts.Length; i++)
{
SceneObjectPart obPart = parts[i];

View File

@@ -266,8 +266,8 @@ namespace OpenSim.Region.Framework.Scenes
private string m_sitAnimation = "SIT";
private string m_text = String.Empty;
private string m_touchName = String.Empty;
private readonly Stack<UndoState> m_undo = new Stack<UndoState>(5);
private readonly Stack<UndoState> m_redo = new Stack<UndoState>(5);
private readonly List<UndoState> m_undo = new List<UndoState>(5);
private readonly List<UndoState> m_redo = new List<UndoState>(5);
private bool m_passTouches = false;
private bool m_passCollisions = false;
@@ -2368,16 +2368,20 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="scale"></param>
public void Resize(Vector3 scale)
{
scale.X = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.X));
scale.Y = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Y));
scale.Z = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Z));
PhysicsActor pa = PhysActor;
if (pa != null && pa.IsPhysical)
if (ParentGroup.Scene != null)
{
scale.X = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.X));
scale.Y = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Y));
scale.Z = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Z));
scale.X = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.X));
scale.Y = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Y));
scale.Z = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Z));
if (pa != null && pa.IsPhysical)
{
scale.X = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.X));
scale.Y = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Y));
scale.Z = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Z));
}
}
// m_log.DebugFormat("[SCENE OBJECT PART]: Resizing {0} {1} to {2}", Name, LocalId, scale);
@@ -3166,61 +3170,62 @@ namespace OpenSim.Region.Framework.Scenes
public void StoreUndoState(bool forGroup)
{
if (!Undoing)
if (ParentGroup == null || ParentGroup.Scene == null)
return;
if (Undoing)
{
if (!IgnoreUndoUpdate)
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Ignoring undo store for {0} {1} since already undoing", Name, LocalId);
return;
}
if (IgnoreUndoUpdate)
{
// m_log.DebugFormat("[SCENE OBJECT PART]: Ignoring undo store for {0} {1}", Name, LocalId);
return;
}
lock (m_undo)
{
if (m_undo.Count > 0)
{
if (ParentGroup != null)
UndoState last = m_undo[m_undo.Count - 1];
if (last != null)
{
lock (m_undo)
// TODO: May need to fix for group comparison
if (last.Compare(this))
{
if (m_undo.Count > 0)
{
UndoState last = m_undo.Peek();
if (last != null)
{
// TODO: May need to fix for group comparison
if (last.Compare(this))
{
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Not storing undo for {0} {1} since current state is same as last undo state, initial stack size {2}",
// Name, LocalId, m_undo.Count);
return;
}
}
}
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Storing undo state for {0} {1}, forGroup {2}, initial stack size {3}",
// Name, LocalId, forGroup, m_undo.Count);
if (ParentGroup.GetSceneMaxUndo() > 0)
{
UndoState nUndo = new UndoState(this, forGroup);
m_undo.Push(nUndo);
if (m_redo.Count > 0)
m_redo.Clear();
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Stored undo state for {0} {1}, forGroup {2}, stack size now {3}",
// Name, LocalId, forGroup, m_undo.Count);
}
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Not storing undo for {0} {1} since current state is same as last undo state, initial stack size {2}",
// Name, LocalId, m_undo.Count);
return;
}
}
}
// else
// {
// m_log.DebugFormat("[SCENE OBJECT PART]: Ignoring undo store for {0} {1}", Name, LocalId);
// }
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Storing undo state for {0} {1}, forGroup {2}, initial stack size {3}",
// Name, LocalId, forGroup, m_undo.Count);
if (ParentGroup.Scene.MaxUndoCount > 0)
{
UndoState nUndo = new UndoState(this, forGroup);
m_undo.Add(nUndo);
if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
m_undo.RemoveAt(0);
if (m_redo.Count > 0)
m_redo.Clear();
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Stored undo state for {0} {1}, forGroup {2}, stack size now {3}",
// Name, LocalId, forGroup, m_undo.Count);
}
}
// else
// {
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Ignoring undo store for {0} {1} since already undoing", Name, LocalId);
// }
}
/// <summary>
@@ -3245,21 +3250,24 @@ namespace OpenSim.Region.Framework.Scenes
if (m_undo.Count > 0)
{
UndoState goback = m_undo.Pop();
UndoState goback = m_undo[m_undo.Count - 1];
m_undo.RemoveAt(m_undo.Count - 1);
if (goback != null)
UndoState nUndo = null;
if (ParentGroup.Scene.MaxUndoCount > 0)
{
UndoState nUndo = null;
if (ParentGroup.GetSceneMaxUndo() > 0)
{
nUndo = new UndoState(this, goback.ForGroup);
}
nUndo = new UndoState(this, goback.ForGroup);
}
goback.PlaybackState(this);
goback.PlaybackState(this);
if (nUndo != null)
m_redo.Push(nUndo);
if (nUndo != null)
{
m_redo.Add(nUndo);
if (m_redo.Count > ParentGroup.Scene.MaxUndoCount)
m_redo.RemoveAt(0);
}
}
@@ -3279,20 +3287,21 @@ namespace OpenSim.Region.Framework.Scenes
if (m_redo.Count > 0)
{
UndoState gofwd = m_redo.Pop();
if (gofwd != null)
UndoState gofwd = m_redo[m_redo.Count - 1];
m_redo.RemoveAt(m_redo.Count - 1);
if (ParentGroup.Scene.MaxUndoCount > 0)
{
if (ParentGroup.GetSceneMaxUndo() > 0)
{
UndoState nUndo = new UndoState(this, gofwd.ForGroup);
m_undo.Push(nUndo);
}
gofwd.PlayfwdState(this);
UndoState nUndo = new UndoState(this, gofwd.ForGroup);
m_undo.Add(nUndo);
if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
m_undo.RemoveAt(0);
}
gofwd.PlayfwdState(this);
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Handled redo request for {0} {1}, stack size now {2}",
// Name, LocalId, m_redo.Count);

View File

@@ -47,6 +47,7 @@ namespace OpenSim.Region.Framework.Scenes
= log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates";
public const string SlowFramesStatName = "SlowFrames";
public delegate void SendStatResult(SimStats stats);
@@ -128,6 +129,16 @@ namespace OpenSim.Region.Framework.Scenes
get { return lastReportedSimStats; }
}
/// <summary>
/// Number of frames that have taken longer to process than Scene.MIN_FRAME_TIME
/// </summary>
public Stat SlowFramesStat { get; private set; }
/// <summary>
/// The threshold at which we log a slow frame.
/// </summary>
public int SlowFramesStatReportThreshold { get; private set; }
/// <summary>
/// Extra sim statistics that are used by monitors but not sent to the client.
/// </summary>
@@ -225,6 +236,22 @@ namespace OpenSim.Region.Framework.Scenes
if (StatsManager.SimExtraStats != null)
OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket;
/// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
/// longer than ideal (which in itself is a concern).
SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2);
SlowFramesStat
= new Stat(
"SlowFrames",
"Slow Frames",
"frames",
"scene",
m_scene.Name,
StatVerbosity.Info,
"Number of frames where frame time has been significantly longer than the desired frame time.");
StatsManager.RegisterStat(SlowFramesStat);
}
public void Close()
@@ -418,6 +445,7 @@ namespace OpenSim.Region.Framework.Scenes
lock (m_lastReportedExtraSimStats)
{
m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor;
m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value;
Dictionary<string, float> physicsStats = m_scene.PhysicsScene.GetStats();
@@ -535,6 +563,11 @@ namespace OpenSim.Region.Framework.Scenes
public void addFrameMS(int ms)
{
m_frameMS += ms;
// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
// longer than ideal due to the inaccuracy of the Sleep in Scene.Update() (which in itself is a concern).
if (ms > SlowFramesStatReportThreshold)
SlowFramesStat.Value++;
}
public void AddSpareMS(int ms)

View File

@@ -62,8 +62,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(g1Post.RootPart.Scale.X, Is.EqualTo(2));
Assert.That(g1Post.RootPart.Scale.Y, Is.EqualTo(3));
Assert.That(g1Post.RootPart.Scale.Z, Is.EqualTo(4));
Assert.That(g1Post.RootPart.UndoCount, Is.EqualTo(1));
}
/// <summary>

View File

@@ -0,0 +1,184 @@
/*
* 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.Reflection;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{
/// <summary>
/// Tests for undo/redo
/// </summary>
public class SceneObjectUndoRedoTests : OpenSimTestCase
{
[Test]
public void TestUndoRedoResizeSceneObject()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
Vector3 firstSize = new Vector3(2, 3, 4);
Vector3 secondSize = new Vector3(5, 6, 7);
Scene scene = new SceneHelpers().SetupScene();
scene.MaxUndoCount = 20;
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
// TODO: It happens to be the case that we are not storing undo states for SOPs which are not yet in a SOG,
// which is the way that AddSceneObject() sets up the object (i.e. it creates the SOP first). However,
// this is somewhat by chance. Really, we shouldn't be storing undo states at all if the object is not
// in a scene.
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
g1.GroupResize(firstSize);
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
g1.GroupResize(secondSize);
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(2));
g1.RootPart.Undo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
Assert.That(g1.GroupScale, Is.EqualTo(firstSize));
g1.RootPart.Redo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(2));
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
}
[Test]
public void TestUndoLimit()
{
TestHelpers.InMethod();
Vector3 firstSize = new Vector3(2, 3, 4);
Vector3 secondSize = new Vector3(5, 6, 7);
Vector3 thirdSize = new Vector3(8, 9, 10);
Vector3 fourthSize = new Vector3(11, 12, 13);
Scene scene = new SceneHelpers().SetupScene();
scene.MaxUndoCount = 2;
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
g1.GroupResize(firstSize);
g1.GroupResize(secondSize);
g1.GroupResize(thirdSize);
g1.GroupResize(fourthSize);
g1.RootPart.Undo();
g1.RootPart.Undo();
g1.RootPart.Undo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
}
[Test]
public void TestNoUndoOnObjectsNotInScene()
{
TestHelpers.InMethod();
Vector3 firstSize = new Vector3(2, 3, 4);
Vector3 secondSize = new Vector3(5, 6, 7);
Vector3 thirdSize = new Vector3(8, 9, 10);
Vector3 fourthSize = new Vector3(11, 12, 13);
Scene scene = new SceneHelpers().SetupScene();
scene.MaxUndoCount = 20;
SceneObjectGroup g1 = SceneHelpers.CreateSceneObject(1, TestHelpers.ParseTail(0x1));
g1.GroupResize(firstSize);
g1.GroupResize(secondSize);
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
g1.RootPart.Undo();
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
}
[Test]
public void TestUndoBeyondAvailable()
{
TestHelpers.InMethod();
Vector3 newSize = new Vector3(2, 3, 4);
Scene scene = new SceneHelpers().SetupScene();
scene.MaxUndoCount = 20;
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
Vector3 originalSize = g1.GroupScale;
g1.RootPart.Undo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
g1.GroupResize(newSize);
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
Assert.That(g1.GroupScale, Is.EqualTo(newSize));
g1.RootPart.Undo();
g1.RootPart.Undo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
}
[Test]
public void TestRedoBeyondAvailable()
{
TestHelpers.InMethod();
Vector3 newSize = new Vector3(2, 3, 4);
Scene scene = new SceneHelpers().SetupScene();
scene.MaxUndoCount = 20;
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
Vector3 originalSize = g1.GroupScale;
g1.RootPart.Redo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
g1.GroupResize(newSize);
g1.RootPart.Undo();
g1.RootPart.Redo();
g1.RootPart.Redo();
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
Assert.That(g1.GroupScale, Is.EqualTo(newSize));
}
}
}

View File

@@ -44,7 +44,7 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{
[TestFixture]
public class SceneObjectUserGroupTests
public class SceneObjectUserGroupTests : OpenSimTestCase
{
/// <summary>
/// Test share with group object functionality
@@ -54,7 +54,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
public void TestShareWithGroup()
{
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
UUID userId = UUID.Parse("10000000-0000-0000-0000-000000000001");

View File

@@ -52,26 +52,23 @@ namespace OpenSim.Region.Framework.Scenes
public class UuidGatherer
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Asset cache used for gathering assets
/// </summary>
protected IAssetService m_assetCache;
/// <summary>
/// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate
/// asset was found by the asset service.
/// </summary>
private AssetBase m_requestedObjectAsset;
/// <summary>
/// Signal whether we are currently waiting for the asset service to deliver an asset.
/// </summary>
private bool m_waitingForObjectAsset;
protected IAssetService m_assetService;
// /// <summary>
// /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate
// /// asset was found by the asset service.
// /// </summary>
// private AssetBase m_requestedObjectAsset;
//
// /// <summary>
// /// Signal whether we are currently waiting for the asset service to deliver an asset.
// /// </summary>
// private bool m_waitingForObjectAsset;
public UuidGatherer(IAssetService assetCache)
{
m_assetCache = assetCache;
m_assetService = assetCache;
}
/// <summary>
@@ -195,18 +192,18 @@ namespace OpenSim.Region.Framework.Scenes
}
}
/// <summary>
/// The callback made when we request the asset for an object from the asset service.
/// </summary>
private void AssetReceived(string id, Object sender, AssetBase asset)
{
lock (this)
{
m_requestedObjectAsset = asset;
m_waitingForObjectAsset = false;
Monitor.Pulse(this);
}
}
// /// <summary>
// /// The callback made when we request the asset for an object from the asset service.
// /// </summary>
// private void AssetReceived(string id, Object sender, AssetBase asset)
// {
// lock (this)
// {
// m_requestedObjectAsset = asset;
// m_waitingForObjectAsset = false;
// Monitor.Pulse(this);
// }
// }
/// <summary>
/// Get an asset synchronously, potentially using an asynchronous callback. If the
@@ -216,25 +213,29 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns>
protected virtual AssetBase GetAsset(UUID uuid)
{
m_waitingForObjectAsset = true;
m_assetCache.Get(uuid.ToString(), this, AssetReceived);
return m_assetService.Get(uuid.ToString());
// The asset cache callback can either
//
// 1. Complete on the same thread (if the asset is already in the cache) or
// 2. Come in via a different thread (if we need to go fetch it).
//
// The code below handles both these alternatives.
lock (this)
{
if (m_waitingForObjectAsset)
{
Monitor.Wait(this);
m_waitingForObjectAsset = false;
}
}
return m_requestedObjectAsset;
// XXX: Switching to do this synchronously where the call was async before but we always waited for it
// to complete anyway!
// m_waitingForObjectAsset = true;
// m_assetCache.Get(uuid.ToString(), this, AssetReceived);
//
// // The asset cache callback can either
// //
// // 1. Complete on the same thread (if the asset is already in the cache) or
// // 2. Come in via a different thread (if we need to go fetch it).
// //
// // The code below handles both these alternatives.
// lock (this)
// {
// if (m_waitingForObjectAsset)
// {
// Monitor.Wait(this);
// m_waitingForObjectAsset = false;
// }
// }
//
// return m_requestedObjectAsset;
}
/// <summary>
@@ -365,4 +366,47 @@ namespace OpenSim.Region.Framework.Scenes
}
}
}
public class HGUuidGatherer : UuidGatherer
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected string m_assetServerURL;
public HGUuidGatherer(IAssetService assetService, string assetServerURL)
: base(assetService)
{
m_assetServerURL = assetServerURL;
if (!m_assetServerURL.EndsWith("/") && !m_assetServerURL.EndsWith("="))
m_assetServerURL = m_assetServerURL + "/";
}
protected override AssetBase GetAsset(UUID uuid)
{
if (string.Empty == m_assetServerURL)
return base.GetAsset(uuid);
else
return FetchAsset(uuid);
}
public AssetBase FetchAsset(UUID assetID)
{
// Test if it's already here
AssetBase asset = m_assetService.Get(assetID.ToString());
if (asset == null)
{
// It's not, so fetch it from abroad
asset = m_assetService.Get(m_assetServerURL + assetID.ToString());
if (asset != null)
m_log.DebugFormat("[HGUUIDGatherer]: Copied asset {0} from {1} to local asset server", assetID, m_assetServerURL);
else
m_log.DebugFormat("[HGUUIDGatherer]: Failed to fetch asset {0} from {1}", assetID, m_assetServerURL);
}
//else
// m_log.DebugFormat("[HGUUIDGatherer]: Asset {0} from {1} was already here", assetID, m_assetServerURL);
return asset;
}
}
}

View File

@@ -130,37 +130,37 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
SendConsoleOutput(agentID, String.Format("auto_grant_attach_perms set to {0}", val));
}
private void llAttachToAvatarTemp(UUID host, UUID script, int attachmentPoint)
private int llAttachToAvatarTemp(UUID host, UUID script, int attachmentPoint)
{
SceneObjectPart hostPart = m_scene.GetSceneObjectPart(host);
if (hostPart == null)
return;
return 0;
if (hostPart.ParentGroup.IsAttachment)
return;
return 0;
IAttachmentsModule attachmentsModule = m_scene.RequestModuleInterface<IAttachmentsModule>();
if (attachmentsModule == null)
return;
return 0;
TaskInventoryItem item = hostPart.Inventory.GetInventoryItem(script);
if (item == null)
return;
return 0;
if ((item.PermsMask & 32) == 0) // PERMISSION_ATTACH
return;
return 0;
ScenePresence target;
if (!m_scene.TryGetScenePresence(item.PermsGranter, out target))
return;
return 0;
if (target.UUID != hostPart.ParentGroup.OwnerID)
{
uint effectivePerms = hostPart.ParentGroup.GetEffectivePermissions();
if ((effectivePerms & (uint)PermissionMask.Transfer) == 0)
return;
return 0;
hostPart.ParentGroup.SetOwnerId(target.UUID);
hostPart.ParentGroup.SetRootPartOwner(hostPart.ParentGroup.RootPart, target.UUID, target.ControllingClient.ActiveGroupId);
@@ -183,7 +183,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
hostPart.ParentGroup.RootPart.ScheduleFullUpdate();
}
attachmentsModule.AttachObject(target, hostPart.ParentGroup, (uint)attachmentPoint, false, true);
return attachmentsModule.AttachObject(target, hostPart.ParentGroup, (uint)attachmentPoint, false, true) ? 1 : 0;
}
}
}

View File

@@ -231,12 +231,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null)
throw new Exception("Invalid connector configuration");
// Generate an initial nickname if randomizing is enabled
// Generate an initial nickname
if (m_randomizeNick)
{
m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
}
else
m_nick = m_baseNick;
m_log.InfoFormat("[IRC-Connector-{0}]: Initialization complete", idn);

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

@@ -34,6 +34,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public class BS6DofConstraint : BSConstraint
{
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
// Create a btGeneric6DofConstraint
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 frame1, Quaternion frame1rot,
@@ -44,11 +48,14 @@ public class BS6DofConstraint : BSConstraint
m_body1 = obj1;
m_body2 = obj2;
m_constraint = new BulletConstraint(
BulletSimAPI.Create6DofConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
frame1, frame1rot,
frame2, frame2rot,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
m_enabled = true;
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
BSScene.DetailLogZero, world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
}
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
@@ -58,11 +65,36 @@ public class BS6DofConstraint : BSConstraint
m_world = world;
m_body1 = obj1;
m_body2 = obj2;
m_constraint = new BulletConstraint(
BulletSimAPI.Create6DofConstraintToPoint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
joinPoint,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
m_enabled = true;
if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero)
{
world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
BSScene.DetailLogZero, world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
"[BULLETSIM 6DOF CONSTRAINT]", world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
m_enabled = false;
}
else
{
m_constraint = new BulletConstraint(
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
joinPoint,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"),
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
if (m_constraint.ptr == IntPtr.Zero)
{
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
LogHeader, obj1.ID, obj2.ID);
m_enabled = false;
}
else
{
m_enabled = true;
}
}
}
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
@@ -70,7 +102,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false;
if (m_enabled)
{
BulletSimAPI.SetFrames2(m_constraint.Ptr, frameA, frameArot, frameB, frameBrot);
BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
ret = true;
}
return ret;
@@ -81,9 +113,9 @@ public class BS6DofConstraint : BSConstraint
bool ret = false;
if (m_enabled)
{
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
ret = true;
}
return ret;
@@ -94,7 +126,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false;
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
if (m_enabled)
ret = BulletSimAPI.UseFrameOffset2(m_constraint.Ptr, onOff);
ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
return ret;
}
@@ -103,7 +135,7 @@ public class BS6DofConstraint : BSConstraint
bool ret = false;
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
if (m_enabled)
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.Ptr, onOff, targetVelocity, maxMotorForce);
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
return ret;
}
@@ -111,7 +143,7 @@ public class BS6DofConstraint : BSConstraint
{
bool ret = false;
if (m_enabled)
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.Ptr, threshold);
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
return ret;
}
}

View File

@@ -28,7 +28,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using OpenMetaverse;
using OMV = OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;
@@ -39,47 +39,35 @@ public class BSCharacter : BSPhysObject
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS CHAR]";
public BSScene Scene { get; private set; }
private String _avName;
// private bool _stopped;
private Vector3 _size;
private Vector3 _scale;
private OMV.Vector3 _size;
private OMV.Vector3 _scale;
private PrimitiveBaseShape _pbs;
private uint _localID = 0;
private bool _grabbed;
private bool _selected;
private Vector3 _position;
private OMV.Vector3 _position;
private float _mass;
public float _density;
public float _avatarVolume;
private Vector3 _force;
private Vector3 _velocity;
private Vector3 _torque;
private float _avatarDensity;
private float _avatarVolume;
private OMV.Vector3 _force;
private OMV.Vector3 _velocity;
private OMV.Vector3 _torque;
private float _collisionScore;
private Vector3 _acceleration;
private Quaternion _orientation;
private OMV.Vector3 _acceleration;
private OMV.Quaternion _orientation;
private int _physicsActorType;
private bool _isPhysical;
private bool _flying;
private bool _setAlwaysRun;
private bool _throttleUpdates;
private bool _isColliding;
private long _collidingStep;
private bool _collidingGround;
private long _collidingGroundStep;
private bool _collidingObj;
private bool _floatOnWater;
private Vector3 _rotationalVelocity;
private OMV.Vector3 _rotationalVelocity;
private bool _kinematic;
private float _buoyancy;
public override BulletBody Body { get; set; }
public override BSLinkset Linkset { get; set; }
private int _subscribedEventsMs = 0;
private int _nextCollisionOkTime = 0;
private Vector3 _PIDTarget;
private OMV.Vector3 _PIDTarget;
private bool _usePID;
private float _PIDTau;
private bool _useHoverPID;
@@ -87,27 +75,26 @@ public class BSCharacter : BSPhysObject
private PIDHoverType _PIDHoverType;
private float _PIDHoverTao;
public BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying)
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
{
_localID = localID;
_avName = avName;
Scene = parent_scene;
base.BaseInitialize(parent_scene, localID, avName, "BSCharacter");
_physicsActorType = (int)ActorTypes.Agent;
_position = pos;
_size = size;
_flying = isFlying;
_orientation = Quaternion.Identity;
_velocity = Vector3.Zero;
_orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero;
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
// The dimensions of the avatar capsule are kept in the scale.
// Physics creates a unit capsule which is scaled by the physics engine.
_scale = new Vector3(Scene.Params.avatarCapsuleRadius, Scene.Params.avatarCapsuleRadius, size.Z);
_density = Scene.Params.avatarDensity;
ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale
Linkset = new BSLinkset(Scene, this);
ComputeAvatarScale(_size);
_avatarDensity = PhysicsScene.Params.avatarDensity;
// set _avatarVolume and _mass based on capsule size, _density and _scale
ComputeAvatarVolumeAndMass();
ShapeData shapeData = new ShapeData();
shapeData.ID = _localID;
shapeData.ID = LocalID;
shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR;
shapeData.Position = _position;
shapeData.Rotation = _orientation;
@@ -116,23 +103,25 @@ public class BSCharacter : BSPhysObject
shapeData.Mass = _mass;
shapeData.Buoyancy = _buoyancy;
shapeData.Static = ShapeData.numericFalse;
shapeData.Friction = Scene.Params.avatarFriction;
shapeData.Restitution = Scene.Params.avatarRestitution;
shapeData.Friction = PhysicsScene.Params.avatarFriction;
shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
// do actual create at taint time
Scene.TaintedObject("BSCharacter.create", delegate()
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
{
DetailLog("{0},BSCharacter.create", _localID);
BulletSimAPI.CreateObject(Scene.WorldID, shapeData);
DetailLog("{0},BSCharacter.create,taint", LocalID);
BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData);
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#.
// If not set at creation, the avatar will stop flying when created after crossing a region boundry.
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.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(PhysicsScene.World.ptr, LocalID));
// This works here because CreateObject has already put the character into the physical world.
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask);
});
return;
}
@@ -140,9 +129,9 @@ public class BSCharacter : BSPhysObject
public override void Destroy()
{
DetailLog("{0},BSCharacter.Destroy", LocalID);
Scene.TaintedObject("BSCharacter.destroy", delegate()
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
{
BulletSimAPI.DestroyObject(Scene.WorldID, _localID);
BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID);
});
}
@@ -151,71 +140,96 @@ public class BSCharacter : BSPhysObject
base.RequestPhysicsterseUpdate();
}
// No one calls this method so I don't know what it could possibly mean
public override bool Stopped {
get { return false; }
public override bool Stopped {
get { return false; }
}
public override Vector3 Size {
public override OMV.Vector3 Size {
get
{
// Avatar capsule size is kept in the scale parameter.
return new Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
}
set {
set {
// When an avatar's size is set, only the height is changed
// and that really only depends on the radius.
_size = value;
_scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
ComputeAvatarScale(_size);
// TODO: something has to be done with the avatar's vertical position
ComputeAvatarVolumeAndMass();
Scene.TaintedObject("BSCharacter.setSize", delegate()
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{
BulletSimAPI.SetObjectScaleMass(Scene.WorldID, LocalID, _scale, _mass, true);
BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true);
});
}
}
public override PrimitiveBaseShape Shape {
set { _pbs = value;
}
}
public override uint LocalID {
set { _localID = value;
}
get { return _localID; }
}
public override bool Grabbed {
set { _grabbed = value;
}
public override PrimitiveBaseShape Shape {
set { _pbs = value;
}
}
public override bool Selected {
set { _selected = value;
}
public override bool Grabbed {
set { _grabbed = value;
}
}
public override bool Selected {
set { _selected = value;
}
}
public override void CrossingFailure() { return; }
public override void link(PhysicsActor obj) { return; }
public override void delink() { return; }
public override void LockAngularMotion(Vector3 axis) { return; }
public override Vector3 Position {
// Set motion values to zero.
// Do it to the properties so the values get set in the physics engine.
// Push the setting of the values to the viewer.
// Called at taint time!
public override void ZeroMotion()
{
_velocity = OMV.Vector3.Zero;
_acceleration = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine
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) { return; }
public override OMV.Vector3 Position {
get {
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
return _position;
}
return _position;
}
set {
_position = value;
PositionSanityCheck();
Scene.TaintedObject("BSCharacter.setPosition", delegate()
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
{
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
});
}
}
}
public override OMV.Vector3 ForcePosition {
get {
_position = BulletSimAPI.GetPosition2(BSBody.ptr);
return _position;
}
set {
_position = value;
PositionSanityCheck();
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
}
}
// Check that the current position is sane and, if not, modify the position to make it so.
// Check for being below terrain and being out of bounds.
@@ -223,15 +237,24 @@ public class BSCharacter : BSPhysObject
private bool PositionSanityCheck()
{
bool ret = false;
// If below the ground, move the avatar up
float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position);
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
if (Position.Z < terrainHeight)
{
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
_position.Z = terrainHeight + 2.0f;
ret = true;
}
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{
float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
if (Position.Z < waterHeight)
{
_position.Z = waterHeight;
ret = true;
}
}
// TODO: check for out of bounds
return ret;
@@ -240,207 +263,234 @@ public class BSCharacter : BSPhysObject
// A version of the sanity check that also makes sure a new position value is
// pushed back to the physics engine. This routine would be used by anyone
// who is not already pushing the value.
private bool PositionSanityCheck2()
private bool PositionSanityCheck2(bool atTaintTime)
{
bool ret = false;
if (PositionSanityCheck())
{
// The new position value must be pushed into the physics engine but we can't
// just assign to "Position" because of potential call loops.
Scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate()
BSScene.TaintCallback sanityOperation = delegate()
{
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
});
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
};
if (atTaintTime)
sanityOperation();
else
PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
ret = true;
}
return ret;
}
public override float Mass {
get {
return _mass;
}
public override float Mass {
get {
return _mass;
}
}
// used when we only want this prim's mass and not the linkset thing
public override float MassRaw { get {return _mass; } }
public override Vector3 Force {
get { return _force; }
public override OMV.Vector3 Force {
get { return _force; }
set {
_force = value;
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
Scene.TaintedObject("BSCharacter.SetForce", delegate()
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
{
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
BulletSimAPI.SetObjectForce(Scene.WorldID, LocalID, _force);
BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force);
});
}
}
}
public override int VehicleType {
get { return 0; }
set { return; }
public override int VehicleType {
get { return 0; }
set { return; }
}
public override void VehicleFloatParam(int param, float value) { }
public override void VehicleVectorParam(int param, Vector3 value) {}
public override void VehicleRotationParam(int param, Quaternion rotation) { }
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
public override void VehicleFlags(int param, bool remove) { }
// Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
public override void SetVolumeDetect(int param) { return; }
public override Vector3 GeometricCenter { get { return Vector3.Zero; } }
public override Vector3 CenterOfMass { get { return Vector3.Zero; } }
public override Vector3 Velocity {
get { return _velocity; }
public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
public override OMV.Vector3 Velocity {
get { return _velocity; }
set {
_velocity = value;
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
Scene.TaintedObject("BSCharacter.setVelocity", delegate()
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
{
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
BulletSimAPI.SetObjectVelocity(Scene.WorldID, _localID, _velocity);
BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity);
});
}
}
}
public override Vector3 Torque {
get { return _torque; }
set { _torque = value;
}
public override OMV.Vector3 Torque {
get { return _torque; }
set { _torque = value;
}
}
public override float CollisionScore {
get { return _collisionScore; }
set { _collisionScore = value;
}
public override float CollisionScore {
get { return _collisionScore; }
set { _collisionScore = value;
}
}
public override Vector3 Acceleration {
public override OMV.Vector3 Acceleration {
get { return _acceleration; }
set { _acceleration = value; }
}
public override Quaternion Orientation {
get { return _orientation; }
public override OMV.Quaternion Orientation {
get { return _orientation; }
set {
_orientation = value;
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
Scene.TaintedObject("BSCharacter.setOrientation", delegate()
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
{
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
});
}
}
}
public override int PhysicsActorType {
get { return _physicsActorType; }
set { _physicsActorType = value;
}
// Go directly to Bullet to get/set the value.
public override OMV.Quaternion ForceOrientation
{
get
{
_orientation = BulletSimAPI.GetOrientation2(BSBody.ptr);
return _orientation;
}
set
{
_orientation = value;
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
}
}
public override bool IsPhysical {
get { return _isPhysical; }
public override int PhysicsActorType {
get { return _physicsActorType; }
set { _physicsActorType = value;
}
}
public override bool IsPhysical {
get { return _isPhysical; }
set { _isPhysical = value;
}
}
}
public override bool Flying {
get { return _flying; }
public override bool Flying {
get { return _flying; }
set {
_flying = value;
// simulate flying by changing the effect of gravity
this.Buoyancy = ComputeBuoyancyFromFlying(_flying);
}
}
}
// Flying is implimented by changing the avatar's buoyancy.
// Would this be done better with a vehicle type?
private float ComputeBuoyancyFromFlying(bool ifFlying) {
return ifFlying ? 1f : 0f;
}
public override bool
SetAlwaysRun {
get { return _setAlwaysRun; }
set { _setAlwaysRun = value; }
public override bool
SetAlwaysRun {
get { return _setAlwaysRun; }
set { _setAlwaysRun = value; }
}
public override bool ThrottleUpdates {
get { return _throttleUpdates; }
set { _throttleUpdates = value; }
public override bool ThrottleUpdates {
get { return _throttleUpdates; }
set { _throttleUpdates = value; }
}
public override bool IsColliding {
get { return (_collidingStep == Scene.SimulationStep); }
set { _isColliding = value; }
get { return (CollidingStep == PhysicsScene.SimulationStep); }
set { _isColliding = value; }
}
public override bool CollidingGround {
get { return (_collidingGroundStep == Scene.SimulationStep); }
set { _collidingGround = value; }
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
set { CollidingGround = value; }
}
public override bool CollidingObj {
get { return _collidingObj; }
set { _collidingObj = value; }
public override bool CollidingObj {
get { return _collidingObj; }
set { _collidingObj = value; }
}
public override bool FloatOnWater {
set { _floatOnWater = value; }
public override bool FloatOnWater {
set {
_floatOnWater = value;
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
{
if (_floatOnWater)
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
else
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
});
}
}
public override Vector3 RotationalVelocity {
get { return _rotationalVelocity; }
set { _rotationalVelocity = value; }
public override OMV.Vector3 RotationalVelocity {
get { return _rotationalVelocity; }
set { _rotationalVelocity = value; }
}
public override bool Kinematic {
get { return _kinematic; }
set { _kinematic = value; }
public override bool Kinematic {
get { return _kinematic; }
set { _kinematic = value; }
}
// neg=fall quickly, 0=1g, 1=0g, pos=float up
public override float Buoyancy {
get { return _buoyancy; }
set { _buoyancy = value;
Scene.TaintedObject("BSCharacter.setBuoyancy", delegate()
public override float Buoyancy {
get { return _buoyancy; }
set { _buoyancy = value;
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
{
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
});
}
}
}
// Used for MoveTo
public override Vector3 PIDTarget {
set { _PIDTarget = value; }
public override OMV.Vector3 PIDTarget {
set { _PIDTarget = value; }
}
public override bool PIDActive {
set { _usePID = value; }
public override bool PIDActive {
set { _usePID = value; }
}
public override float PIDTau {
set { _PIDTau = value; }
public override float PIDTau {
set { _PIDTau = value; }
}
// Used for llSetHoverHeight and maybe vehicle height
// Hover Height will override MoveTo target's Z
public override bool PIDHoverActive {
public override bool PIDHoverActive {
set { _useHoverPID = value; }
}
public override float PIDHoverHeight {
public override float PIDHoverHeight {
set { _PIDHoverHeight = value; }
}
public override PIDHoverType PIDHoverType {
public override PIDHoverType PIDHoverType {
set { _PIDHoverType = value; }
}
public override float PIDHoverTau {
public override float PIDHoverTau {
set { _PIDHoverTao = value; }
}
// For RotLookAt
public override Quaternion APIDTarget { set { return; } }
public override OMV.Quaternion APIDTarget { set { return; } }
public override bool APIDActive { set { return; } }
public override float APIDStrength { set { return; } }
public override float APIDDamping { set { return; } }
public override void AddForce(Vector3 force, bool pushforce) {
public override void AddForce(OMV.Vector3 force, bool pushforce) {
if (force.IsFinite())
{
_force.X += force.X;
_force.Y += force.Y;
_force.Z += force.Z;
// m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
Scene.TaintedObject("BSCharacter.AddForce", delegate()
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
{
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
BulletSimAPI.AddObjectForce2(Body.Ptr, _force);
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
});
}
else
@@ -450,43 +500,19 @@ public class BSCharacter : BSPhysObject
//m_lastUpdateSent = false;
}
public override void AddAngularForce(Vector3 force, bool pushforce) {
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
}
public override void SetMomentum(Vector3 momentum) {
public override void SetMomentum(OMV.Vector3 momentum) {
}
// Turn on collision events at a rate no faster than one every the given milliseconds
public override void SubscribeEvents(int ms) {
_subscribedEventsMs = ms;
if (ms > 0)
{
// make sure first collision happens
_nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs;
Scene.TaintedObject("BSCharacter.SubscribeEvents", delegate()
{
BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
}
public override void ZeroMotion()
private void ComputeAvatarScale(OMV.Vector3 size)
{
return;
}
_scale.X = PhysicsScene.Params.avatarCapsuleRadius;
_scale.Y = PhysicsScene.Params.avatarCapsuleRadius;
// 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);
// });
}
// Return 'true' if someone has subscribed to events
public override bool SubscribedEvents() {
return (_subscribedEventsMs > 0);
// The 1.15 came from ODE but it seems to cause the avatar to float off the ground
// _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
_scale.Z = (_size.Z) - (_scale.X + _scale.Y);
}
// set _avatarVolume and _mass based on capsule size, _density and _scale
@@ -503,7 +529,7 @@ public class BSCharacter : BSPhysObject
* Math.Min(_scale.X, _scale.Y)
* _scale.Y // plus the volume of the capsule end caps
);
_mass = _density * _avatarVolume;
_mass = _avatarDensity * _avatarVolume;
}
// The physics engine says that properties have updated. Update same and inform
@@ -515,71 +541,14 @@ public class BSCharacter : BSPhysObject
_velocity = entprop.Velocity;
_acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity;
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
PositionSanityCheck2(true);
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
// base.RequestPhysicsterseUpdate();
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
PositionSanityCheck2();
float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity, heightHere);
}
// Called by the scene when a collision with this object is reported
// 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)
{
// m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
// The following makes IsColliding() and IsCollidingGround() work
_collidingStep = Scene.SimulationStep;
if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
{
_collidingGroundStep = Scene.SimulationStep;
}
// DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
// throttle collisions to the rate specified in the subscription
if (_subscribedEventsMs != 0) {
int nowTime = Scene.SimulationNowTime;
if (nowTime >= _nextCollisionOkTime) {
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
if (collisionCollection == null)
collisionCollection = new CollisionEventUpdate();
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
}
}
}
public override void SendCollisions()
{
/*
if (collisionCollection != null && collisionCollection.Count > 0)
{
base.SendCollisionUpdate(collisionCollection);
collisionCollection = null;
}
*/
// Kludge to make a collision call even if there are no collisions.
// This causes the avatar animation to get updated.
if (collisionCollection == null)
collisionCollection = new CollisionEventUpdate();
base.SendCollisionUpdate(collisionCollection);
// If there were any collisions in the collection, make sure we don't use the
// same instance next time.
if (collisionCollection.Count > 0)
collisionCollection = null;
// End kludge
}
// Invoke the detailed logger and output something if it's enabled.
private void DetailLog(string msg, params Object[] args)
{
Scene.PhysicsLogging.Write(msg, args);
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
}
}
}

View File

@@ -49,20 +49,23 @@ public abstract class BSConstraint : IDisposable
if (m_enabled)
{
m_enabled = false;
bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr);
m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
m_constraint.Ptr = System.IntPtr.Zero;
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
m_constraint.ptr = System.IntPtr.Zero;
}
}
public BulletBody Body1 { get { return m_body1; } }
public BulletBody Body2 { get { return m_body2; } }
public BulletConstraint Constraint { get { return m_constraint; } }
public abstract ConstraintType Type { get; }
public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
{
bool ret = false;
if (m_enabled)
ret = BulletSimAPI.SetLinearLimits2(m_constraint.Ptr, low, high);
ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
return ret;
}
@@ -70,7 +73,18 @@ public abstract class BSConstraint : IDisposable
{
bool ret = false;
if (m_enabled)
ret = BulletSimAPI.SetAngularLimits2(m_constraint.Ptr, low, high);
ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
return ret;
}
public virtual bool SetSolverIterations(float cnt)
{
bool ret = false;
if (m_enabled)
{
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
ret = true;
}
return ret;
}
@@ -80,7 +94,7 @@ public abstract class BSConstraint : IDisposable
if (m_enabled)
{
// Recompute the internal transforms
BulletSimAPI.CalculateTransforms2(m_constraint.Ptr);
BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
ret = true;
}
return ret;
@@ -96,13 +110,14 @@ 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);
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
// 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.physicsScene.NumericBool(true));
}
else
{
m_world.scene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID);
m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID);
}
}
return ret;

View File

@@ -54,18 +54,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSDynamics
{
private int frcount = 0; // Used to limit dynamics debug output to
// every 100th frame
private BSScene m_physicsScene;
private BSPrim m_prim; // the prim this dynamic controller belongs to
private BSScene PhysicsScene { get; set; }
// the prim this dynamic controller belongs to
private BSPrim Prim { get; set; }
// Vehicle properties
private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind
public Vehicle Type
{
get { return m_type; }
}
public Vehicle Type { get; set; }
// private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier
private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings:
// HOVER_TERRAIN_ONLY
@@ -80,7 +75,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;
@@ -97,7 +92,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body
// private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
//Deflection properties
// private float m_angularDeflectionEfficiency = 0;
@@ -126,85 +121,72 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public BSDynamics(BSScene myScene, BSPrim myPrim)
{
m_physicsScene = myScene;
m_prim = myPrim;
m_type = Vehicle.TYPE_NONE;
PhysicsScene = myScene;
Prim = myPrim;
Type = Vehicle.TYPE_NONE;
}
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep)
// Return 'true' if this vehicle is doing vehicle things
public bool IsActive
{
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
get { return Type != Vehicle.TYPE_NONE; }
}
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
{
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
switch (pParam)
{
case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
if (pValue < 0.01f) pValue = 0.01f;
// m_angularDeflectionEfficiency = pValue;
// m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f);
break;
case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
// m_angularDeflectionTimescale = pValue;
// m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_angularMotorDecayTimescale = pValue;
m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.ANGULAR_MOTOR_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_angularMotorTimescale = pValue;
m_angularMotorTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.BANKING_EFFICIENCY:
if (pValue < 0.01f) pValue = 0.01f;
// m_bankingEfficiency = pValue;
// m_bankingEfficiency = Math.Max(pValue, 0.01f);
break;
case Vehicle.BANKING_MIX:
if (pValue < 0.01f) pValue = 0.01f;
// m_bankingMix = pValue;
// m_bankingMix = Math.Max(pValue, 0.01f);
break;
case Vehicle.BANKING_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
// m_bankingTimescale = pValue;
// m_bankingTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.BUOYANCY:
if (pValue < -1f) pValue = -1f;
if (pValue > 1f) pValue = 1f;
m_VehicleBuoyancy = pValue;
m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f));
break;
// case Vehicle.HOVER_EFFICIENCY:
// if (pValue < 0f) pValue = 0f;
// if (pValue > 1f) pValue = 1f;
// m_VhoverEfficiency = pValue;
// m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f));
// break;
case Vehicle.HOVER_HEIGHT:
m_VhoverHeight = pValue;
break;
case Vehicle.HOVER_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_VhoverTimescale = pValue;
m_VhoverTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
if (pValue < 0.01f) pValue = 0.01f;
// m_linearDeflectionEfficiency = pValue;
// m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f);
break;
case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
// m_linearDeflectionTimescale = pValue;
// m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_linearMotorDecayTimescale = pValue;
m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.LINEAR_MOTOR_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_linearMotorTimescale = pValue;
m_linearMotorTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable
if (pValue > 1.0f) pValue = 1.0f;
m_verticalAttractionEfficiency = pValue;
m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f));
break;
case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
if (pValue < 0.01f) pValue = 0.01f;
m_verticalAttractionTimescale = pValue;
m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
break;
// These are vector properties but the engine lets you use a single float value to
@@ -230,9 +212,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
}//end ProcessFloatVehicleParam
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep)
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
{
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
switch (pParam)
{
case Vehicle.ANGULAR_FRICTION_TIMESCALE:
@@ -267,7 +249,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue)
{
VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
switch (pParam)
{
case Vehicle.REFERENCE_FRAME:
@@ -281,7 +263,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
internal void ProcessVehicleFlags(int pParam, bool remove)
{
VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", m_prim.LocalID, pParam, remove);
VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove);
VehicleFlag parm = (VehicleFlag)pParam;
if (remove)
{
@@ -299,11 +281,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
}//end ProcessVehicleFlags
internal void ProcessTypeChange(Vehicle pType, float stepSize)
internal void ProcessTypeChange(Vehicle pType)
{
VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType);
// Set Defaults For Type
m_type = pType;
Type = pType;
switch (pType)
{
case Vehicle.TYPE_NONE:
@@ -370,8 +352,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_bankingMix = 1;
// m_bankingTimescale = 1;
// m_referenceFrame = Quaternion.Identity;
m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY |
VehicleFlag.LIMIT_MOTOR_UP);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.LIMIT_MOTOR_UP);
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT);
m_flags |= (VehicleFlag.HOVER_UP_ONLY);
break;
@@ -398,12 +381,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_bankingMix = 0.8f;
// m_bankingTimescale = 1;
// m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY |
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP |
VehicleFlag.LIMIT_MOTOR_UP);
m_flags |= (VehicleFlag.HOVER_WATER_ONLY);
m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT
| VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.HOVER_UP_ONLY);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_MOTOR_UP
| VehicleFlag.HOVER_WATER_ONLY);
break;
case Vehicle.TYPE_AIRPLANE:
m_linearFrictionTimescale = new Vector3(200, 10, 5);
@@ -428,9 +412,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_bankingMix = 0.7f;
// m_bankingTimescale = 2;
// m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP);
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
| VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT
| VehicleFlag.HOVER_UP_ONLY
| VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_MOTOR_UP);
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
break;
case Vehicle.TYPE_BALLOON:
@@ -456,51 +443,66 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_bankingMix = 0.7f;
// m_bankingTimescale = 5;
// m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
VehicleFlag.HOVER_UP_ONLY);
m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP);
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
m_flags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT);
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
| VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_UP_ONLY
| VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_MOTOR_UP);
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT);
break;
}
}//end SetDefaultsForType
// Some of the properties of this prim may have changed.
// Do any updating needed for a vehicle
public void Refresh()
{
if (!IsActive)
return;
// Set the prim's inertia to zero. The vehicle code handles that and this
// removes the torque action introduced by Bullet.
Vector3 inertia = Vector3.Zero;
BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
}
// One step of the vehicle properties for the next 'pTimestep' seconds.
internal void Step(float pTimestep)
{
if (m_type == Vehicle.TYPE_NONE) return;
frcount++; // used to limit debug comment output
if (frcount > 100)
frcount = 0;
if (!IsActive) return;
MoveLinear(pTimestep);
MoveAngular(pTimestep);
LimitRotation(pTimestep);
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);
// remember the position so next step we can limit absolute movement effects
m_lastPositionVector = Prim.ForcePosition;
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity);
}// end Step
// Apply the effect of the linear motor.
// Also does hover and float.
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;
// m_lastLinearVelocityVector += (addAmount*10);
m_lastLinearVelocityVector += addAmount;
// This will work temporarily, but we really need to compare speed on an axis
// KF: Limit body velocity to applied velocity?
// Limit the velocity vector to less than the last set linear motor direction
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
@@ -509,53 +511,108 @@ namespace OpenSim.Region.Physics.BulletSPlugin
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
// decay applied velocity
Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep)));
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
/*
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale;
m_lastLinearVelocityVector += addAmount;
float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale);
m_linearMotorDirection *= decayfraction;
// decay applied velocity
Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
// (RA: do not know where the 0.5f comes from)
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
*/
float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
m_linearMotorDirection *= keepfraction;
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}",
m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}",
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", Prim.LocalID);
}
// convert requested object velocity to object relative vector
Quaternion rotq = m_prim.Orientation;
m_dir = m_lastLinearVelocityVector * rotq;
Quaternion rotq = Prim.ForceOrientation;
m_newVelocity = m_lastLinearVelocityVector * rotq;
// Add the various forces into m_dir which will be our new direction vector (velocity)
// add Gravity and Buoyancy
// KF: So far I have found no good method to combine a script-requested
// .Z velocity and gravity. Therefore only 0g will used script-requested
// .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only.
Vector3 grav = Vector3.Zero;
// There is some gravity, make a gravity force vector that is applied after object velocity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy);
Vector3 grav = Prim.PhysicsScene.DefaultGravity * (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 pos = Prim.ForcePosition;
// 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 = Prim.PhysicsScene.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.ForceOrientation;
// if (rotatedSize.Z < terrainHeight)
if (pos.Z < terrainHeight)
{
pos.Z = terrainHeight + 2;
Prim.ForcePosition = pos;
VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", 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 = Prim.PhysicsScene.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)
{
Prim.ForcePosition = 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}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight);
}
Vector3 posChange = pos - m_lastPositionVector;
if (m_BlockingEndPoint != Vector3.Zero)
{
bool changed = false;
@@ -586,134 +643,56 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
if (changed)
{
m_prim.Position = pos;
Prim.ForcePosition = pos;
VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
m_prim.LocalID, m_BlockingEndPoint, posChange, pos);
Prim.LocalID, m_BlockingEndPoint, posChange, pos);
}
}
// 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
}
// Limit absolute vertical change
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;
}
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
{
m_dir.Y = 0;
}
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
{
m_dir.Z = 0;
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", Prim.LocalID, grav);
}
m_lastPositionVector = m_prim.Position;
// If not changing some axis, reduce out velocity
if ((m_flags & (VehicleFlag.NO_X)) != 0)
m_newVelocity.X = 0;
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
m_newVelocity.Y = 0;
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
m_newVelocity.Z = 0;
// Apply velocity
m_prim.Velocity = m_dir;
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}",
Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
} // end MoveLinear()
// Apply the effect of the angular motor.
private void MoveAngular(float pTimestep)
{
// m_angularMotorDirection // angular velocity requested by LSL motor
@@ -725,7 +704,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_lastAngularVelocity // what was last applied to body
// Get what the body is doing, this includes 'external' influences
Vector3 angularVelocity = m_prim.RotationalVelocity;
Vector3 angularVelocity = Prim.RotationalVelocity;
if (m_angularMotorApply > 0)
{
@@ -733,19 +712,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// a newly set velocity, this routine steps the value from the previous
// value (m_angularMotorVelocity) to the requested value (m_angularMotorDirection).
// There are m_angularMotorApply steps.
Vector3 origAngularVelocity = m_angularMotorVelocity;
Vector3 origVel = m_angularMotorVelocity;
Vector3 origDir = m_angularMotorDirection;
// ramp up to new value
// current velocity += error / (time to get there / step interval)
// requested speed - last motor speed
// new 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},origDir={5},vel={6}",
Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origVel, origDir, 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.
m_angularMotorApply--;
}
else
{
@@ -758,25 +738,32 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Vertical attractor section
Vector3 vertattr = Vector3.Zero;
if (m_verticalAttractionTimescale < 300)
Vector3 deflection = Vector3.Zero;
Vector3 banking = Vector3.Zero;
if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
{
float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep);
float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep);
VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
// get present body rotation
Quaternion rotq = m_prim.Orientation;
// make a vector pointing up
Quaternion rotq = Prim.ForceOrientation;
// vector pointing up
Vector3 verterr = Vector3.Zero;
verterr.Z = 1.0f;
// rotate it to Body Angle
verterr = verterr * rotq;
// verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
// verterr.X and .Y are the World error amounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
// As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go
// negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body.
// Error is 0 (no error) to +/- 2 (max error)
if (verterr.Z < 0.0f)
{
verterr.X = 2.0f - verterr.X;
verterr.Y = 2.0f - verterr.Y;
}
// Error is 0 (no error) to +/- 2 (max error)
// scale it by VAservo
verterr = verterr * VAservo;
@@ -791,12 +778,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
vertattr.X += bounce * angularVelocity.X;
vertattr.Y += bounce * angularVelocity.Y;
VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}",
m_prim.LocalID, verterr, bounce, vertattr);
VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}",
Prim.LocalID, verterr, bounce, vertattr);
} // else vertical attractor is off
// m_lastVertAttractor = vertattr;
m_lastVertAttractor = vertattr;
// Bank section tba
@@ -804,18 +791,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Sum velocities
m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
{
m_lastAngularVelocity.X = 0;
m_lastAngularVelocity.Y = 0;
VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity);
VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
}
if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
{
m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero.
VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity);
VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
}
// apply friction
@@ -823,14 +810,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
// Apply to the body
m_prim.RotationalVelocity = m_lastAngularVelocity;
Prim.RotationalVelocity = m_lastAngularVelocity;
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", m_prim.LocalID, decayamount, m_lastAngularVelocity);
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity);
} //end MoveAngular
internal void LimitRotation(float timestep)
{
Quaternion rotq = m_prim.Orientation;
Quaternion rotq = Prim.ForceOrientation;
Quaternion m_rot = rotq;
bool changed = false;
if (m_RollreferenceFrame != Quaternion.Identity)
@@ -863,23 +850,19 @@ 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;
{
Prim.ForceOrientation = m_rot;
VDetailLog("{0},LimitRotation,done,orig={1},new={2}", 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.
private void VDetailLog(string msg, params Object[] args)
{
if (m_prim.Scene.VehicleLoggingEnabled)
m_prim.Scene.PhysicsLogging.Write(msg, args);
if (Prim.PhysicsScene.VehicleLoggingEnabled)
Prim.PhysicsScene.PhysicsLogging.Write(msg, args);
}
}
}

View File

@@ -1,55 +1,57 @@
/*
* 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 copyrightD
* 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.Text;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
class BSHingeConstraint : BSConstraint
{
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB,
Vector3 axisInA, Vector3 axisInB,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
{
m_world = world;
m_body1 = obj1;
m_body2 = obj2;
m_constraint = new BulletConstraint(
BulletSimAPI.CreateHingeConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
pivotInA, pivotInB,
axisInA, axisInB,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
m_enabled = true;
}
}
}
/*
* 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 copyrightD
* 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.Text;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
class BSHingeConstraint : BSConstraint
{
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB,
Vector3 axisInA, Vector3 axisInB,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
{
m_world = world;
m_body1 = obj1;
m_body2 = obj2;
m_constraint = new BulletConstraint(
BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
pivotInA, pivotInB,
axisInA, axisInB,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
m_enabled = true;
}
}
}

View File

@@ -34,19 +34,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSLinkset
{
private static string LogHeader = "[BULLETSIM LINKSET]";
// private static string LogHeader = "[BULLETSIM LINKSET]";
private BSPhysObject m_linksetRoot;
public BSPhysObject LinksetRoot { get { return m_linksetRoot; } }
public BSPhysObject LinksetRoot { get; protected set; }
private BSScene m_physicsScene;
public BSScene PhysicsScene { get { return m_physicsScene; } }
public BSScene PhysicsScene { get; private set; }
static int m_nextLinksetID = 1;
public int LinksetID { get; private set; }
// The children under the root in this linkset
// The children under the root in this linkset.
// There are two lists of children: the current children at runtime
// and the children at taint-time. For instance, if you delink a
// child from the linkset, the child is removed from m_children
// but the constraint won't be removed until taint time.
// Two lists lets this track the 'current' children and
// the physical 'taint' children separately.
// After taint processing and before the simulation step, these
// two lists must be the same.
private List<BSPhysObject> m_children;
private List<BSPhysObject> m_taintChildren;
// We lock the diddling of linkset classes to prevent any badness.
// This locks the modification of the instances of this class. Changes
@@ -55,9 +62,9 @@ public class BSLinkset
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
private float m_mass;
public float LinksetMass
{
get
public float LinksetMass
{
get
{
m_mass = ComputeLinksetMass();
return m_mass;
@@ -79,22 +86,26 @@ public class BSLinkset
// A simple linkset of one (no children)
LinksetID = m_nextLinksetID++;
// We create LOTS of linksets.
if (m_nextLinksetID < 0)
if (m_nextLinksetID <= 0)
m_nextLinksetID = 1;
m_physicsScene = scene;
m_linksetRoot = parent;
PhysicsScene = scene;
LinksetRoot = parent;
m_children = new List<BSPhysObject>();
m_taintChildren = new List<BSPhysObject>();
m_mass = parent.MassRaw;
}
// Link to a linkset where the child knows the parent.
// Parent changing should not happen so do some sanity checking.
// We return the parent's linkset so the child can track its membership.
// Called at runtime.
public BSLinkset AddMeToLinkset(BSPhysObject child)
{
lock (m_linksetActivityLock)
{
AddChildToLinkset(child);
// Don't add the root to its own linkset
if (!IsRoot(child))
AddChildToLinkset(child);
}
return this;
}
@@ -102,26 +113,18 @@ public class BSLinkset
// Remove a child from a linkset.
// Returns a new linkset for the child which is a linkset of one (just the
// orphened child).
// Called at runtime.
public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
{
lock (m_linksetActivityLock)
{
if (IsRoot(child))
{
// if root of linkset, take the linkset apart
while (m_children.Count > 0)
{
// Note that we don't do a foreach because the remove routine
// takes it out of the list.
RemoveChildFromOtherLinkset(m_children[0]);
}
m_children.Clear(); // just to make sure
}
else
{
// Just removing a child from an existing linkset
RemoveChildFromLinkset(child);
// Cannot remove the root from a linkset.
return this;
}
RemoveChildFromLinkset(child);
}
// The child is down to a linkset of just itself
@@ -131,7 +134,7 @@ public class BSLinkset
// Return 'true' if the passed object is the root object of this linkset
public bool IsRoot(BSPhysObject requestor)
{
return (requestor.LocalID == m_linksetRoot.LocalID);
return (requestor.LocalID == LinksetRoot.LocalID);
}
public int NumberOfChildren { get { return m_children.Count; } }
@@ -157,56 +160,34 @@ public class BSLinkset
return ret;
}
private float ComputeLinksetMass()
// 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)
{
float mass = m_linksetRoot.MassRaw;
foreach (BSPhysObject bp in m_children)
{
mass += bp.MassRaw;
}
return mass;
// What is done for each object in BSPrim is what we want.
return false;
}
private OMV.Vector3 ComputeLinksetCenterOfMass()
// 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)
{
OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw;
float totalMass = m_linksetRoot.MassRaw;
lock (m_linksetActivityLock)
{
foreach (BSPhysObject bp in m_children)
{
com += bp.Position * bp.MassRaw;
totalMass += bp.MassRaw;
}
if (totalMass != 0f)
com /= totalMass;
}
return com;
}
private OMV.Vector3 ComputeLinksetGeometricCenter()
{
OMV.Vector3 com = m_linksetRoot.Position;
lock (m_linksetActivityLock)
{
foreach (BSPhysObject bp in m_children)
{
com += bp.Position * bp.MassRaw;
}
com /= (m_children.Count + 1);
}
return com;
// 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.
// Called at runtime.
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;
@@ -220,37 +201,125 @@ public class BSLinkset
}
}
// Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Used when objects are added or removed
// 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()
// Routine used when rebuilding the body of the root of the linkset
// Destroy all the constraints have have been made to root.
// This is called when the root body is changing.
// Returns 'true' of something eas actually removed and would need restoring
// Called at taint-time!!
public bool RemoveBodyDependencies(BSPrim child)
{
float linksetMass = LinksetMass;
bool ret = false;
lock (m_linksetActivityLock)
{
foreach (BSPhysObject child in m_children)
if (IsRoot(child))
{
BSConstraint constrain;
if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.Body, child.Body, out constrain))
// If the one with the dependency is root, must undo all children
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}",
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
foreach (BSPhysObject bpo in m_taintChildren)
{
// DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}",
// LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID);
constrain.RecomputeConstraintVariables(linksetMass);
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
ret = true;
}
else
}
else
{
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
child.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
// Remove the dependency on the body of this one
if (m_taintChildren.Contains(child))
{
// Non-fatal error that can happen 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);
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
ret = true;
}
}
}
return false;
return ret;
}
// Routine used when rebuilding the body of the root of the linkset
// This is called after RemoveAllLinksToRoot() to restore all the constraints.
// This is called when the root body has been changed.
// Called at taint-time!!
public void RestoreBodyDependencies(BSPrim child)
{
lock (m_linksetActivityLock)
{
if (IsRoot(child))
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}",
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
foreach (BSPhysObject bpo in m_taintChildren)
{
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
}
}
else
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
LinksetRoot.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
}
}
}
// ================================================================
// Below this point is internal magic
private float ComputeLinksetMass()
{
float mass;
lock (m_linksetActivityLock)
{
mass = LinksetRoot.MassRaw;
foreach (BSPhysObject bp in m_taintChildren)
{
mass += bp.MassRaw;
}
}
return mass;
}
private OMV.Vector3 ComputeLinksetCenterOfMass()
{
OMV.Vector3 com;
lock (m_linksetActivityLock)
{
com = LinksetRoot.Position * LinksetRoot.MassRaw;
float totalMass = LinksetRoot.MassRaw;
foreach (BSPhysObject bp in m_taintChildren)
{
com += bp.Position * bp.MassRaw;
totalMass += bp.MassRaw;
}
if (totalMass != 0f)
com /= totalMass;
}
return com;
}
private OMV.Vector3 ComputeLinksetGeometricCenter()
{
OMV.Vector3 com;
lock (m_linksetActivityLock)
{
com = LinksetRoot.Position;
foreach (BSPhysObject bp in m_taintChildren)
{
com += bp.Position * bp.MassRaw;
}
com /= (m_taintChildren.Count + 1);
}
return com;
}
// I am the root of a linkset and a new child is being added
@@ -261,12 +330,22 @@ public class BSLinkset
{
m_children.Add(child);
BSPhysObject rootx = LinksetRoot; // capture the root as of now
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
BSPhysObject childx = child;
m_physicsScene.TaintedObject("AddChildToLinkset", delegate()
DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
rootx.LocalID,
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X"));
PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
{
DetailLog("{0},AddChildToLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
PhysicallyLinkAChildToRoot(rootx, childx); // build the physical binding between me and the child
DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID);
// build the physical binding between me and the child
m_taintChildren.Add(childx);
// Since this is taint-time, the body and shape could have changed for the child
PhysicallyLinkAChildToRoot(rootx, rootx.BSBody, childx, childx.BSBody);
});
}
return;
@@ -276,10 +355,10 @@ public class BSLinkset
// This is not being called by the child so we have to make sure the child doesn't think
// it's still connected to the linkset.
// Normal OpenSimulator operation will never do this because other SceneObjectPart information
// has to be updated also (like pointer to prim's parent).
// also has to be updated (like pointer to prim's parent).
private void RemoveChildFromOtherLinkset(BSPhysObject pchild)
{
pchild.Linkset = new BSLinkset(m_physicsScene, pchild);
pchild.Linkset = new BSLinkset(PhysicsScene, pchild);
RemoveChildFromLinkset(pchild);
}
@@ -289,16 +368,23 @@ public class BSLinkset
{
if (m_children.Remove(child))
{
BSPhysObject rootx = LinksetRoot; // capture the root as of now
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
BSPhysObject childx = child;
m_physicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
{
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
PhysicallyUnlinkAChildFromRoot(rootx, childx);
DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
childx.LocalID,
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X"));
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
{
if (m_taintChildren.Contains(childx))
m_taintChildren.Remove(childx);
PhysicallyUnlinkAChildFromRoot(rootx, rootx.BSBody, childx, childx.BSBody);
RecomputeLinksetConstraintVariables();
});
RecomputeLinksetConstraintVariables();
}
else
{
@@ -310,7 +396,8 @@ public class BSLinkset
// Create a constraint between me (root of linkset) and the passed prim (the child).
// Called at taint time!
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody,
BSPhysObject childPrim, BulletBody childBody)
{
// Zero motion for children so they don't interpolate
childPrim.ZeroMotion();
@@ -322,18 +409,36 @@ public class BSLinkset
// real world coordinate of midpoint between the two objects
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
rootPrim.LocalID,
rootPrim.LocalID, rootBody.ptr.ToString("X"),
childPrim.LocalID, childBody.ptr.ToString("X"),
rootPrim.Position, childPrim.Position, midPoint);
// create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
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);
// There is great subtlty in these paramters. Notice the check for a ptr of zero.
// We pass the BulletBody structure into the taint in order to capture the pointer
// of the body at the time of constraint creation. This doesn't work for the very first
// construction because there is no body yet. The body
// is constructed later at taint time. Thus we use the body address at time of the
// taint creation but, if it is zero, use what's in the prim at the moment.
// There is a possible race condition since shape can change without a taint call
// (like changing to a mesh that is already constructed). The fix for that would be
// to only change BSShape at taint time thus syncronizing these operations at
// the cost of efficiency and lag.
BS6DofConstraint constrain = new BS6DofConstraint(
m_physicsScene.World, rootPrim.Body, childPrim.Body,
PhysicsScene.World,
rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody,
childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody,
midPoint,
true,
true
);
/* NOTE: below is an attempt to build constraint with full frame computation, etc.
* Using the midpoint is easier since it lets the Bullet code use the transforms
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms
* of the objects.
* Code left as a warning to future programmers.
// ==================================================================================
@@ -365,7 +470,7 @@ public class BSLinkset
// ==================================================================================
*/
m_physicsScene.Constraints.AddConstraint(constrain);
PhysicsScene.Constraints.AddConstraint(constrain);
// zero linear and angular limits makes the objects unable to move in relation to each other
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
@@ -377,36 +482,88 @@ 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();
}
// Remove linkage between myself and a particular child
// The root and child bodies are passed in because we need to remove the constraint between
// the bodies that were at unlink time.
// Called at taint time!
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody,
BSPhysObject childPrim, BulletBody childBody)
{
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
rootPrim.LocalID,
rootPrim.LocalID, rootBody.ptr.ToString("X"),
childPrim.LocalID, childBody.ptr.ToString("X"));
// 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);
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody);
// 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
/*
// Remove linkage between myself and any possible children I might have.
// Called at taint time!
private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
{
DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.Body);
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody);
}
*/
// Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Used when objects are added or removed
// from a linkset to make sure the constraints know about the new mass and
// geometry.
// Must only be called at taint time!!
private void RecomputeLinksetConstraintVariables()
{
float linksetMass = LinksetMass;
foreach (BSPhysObject child in m_taintChildren)
{
BSConstraint constrain;
if (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);
constrain.RecomputeConstraintVariables(linksetMass);
}
else
{
// Non-fatal error that happens when children are being added to the linkset but
// their constraints have not been created yet.
break;
}
}
// If the whole linkset is not here, doesn't make sense to recompute linkset wide values
if (m_children.Count == m_taintChildren.Count)
{
// 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_taintChildren)
{
BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity);
}
}
return;
}
// Invoke the detailed logger and output something if it's enabled.
private void DetailLog(string msg, params Object[] args)
{
m_physicsScene.PhysicsLogging.Write(msg, args);
PhysicsScene.PhysicsLogging.Write(msg, args);
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
@@ -39,22 +39,175 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// unless the difference is significant.
public abstract class BSPhysObject : PhysicsActor
{
public abstract BSLinkset Linkset { get; set; }
protected void BaseInitialize(BSScene parentScene, uint localID, string name, string typeName)
{
PhysicsScene = parentScene;
LocalID = localID;
PhysObjectName = name;
TypeName = typeName;
public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
public abstract void SendCollisions();
Linkset = new BSLinkset(PhysicsScene, this);
// Return the object mass without calculating it or side effects
CollisionCollection = new CollisionEventUpdate();
SubscribedEventsMs = 0;
CollidingStep = 0;
CollidingGroundStep = 0;
}
public BSScene PhysicsScene { get; protected set; }
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
public string PhysObjectName { get; protected set; }
public string TypeName { get; protected set; }
public BSLinkset Linkset { get; set; }
// Return the object mass without calculating it or having side effects
public abstract float MassRaw { get; }
public abstract BulletBody Body { get; set; }
// Reference to the physical body (btCollisionObject) of this object
public BulletBody BSBody;
// Reference to the physical shape (btCollisionShape) of this object
public BulletShape BSShape;
// Stop all physical motion.
public abstract void ZeroMotion();
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
public virtual void StepVehicle(float timeStep) { }
// Update the physical location and motion of the object. Called with data from Bullet.
public abstract void UpdateProperties(EntityProperties entprop);
// Tell the object to clean up.
public abstract void Destroy();
public abstract OMV.Vector3 ForcePosition { get; set; }
public abstract OMV.Quaternion ForceOrientation { get; set; }
#region Collisions
// Requested number of milliseconds between collision events. Zero means disabled.
protected int SubscribedEventsMs { get; set; }
// Given subscription, the time that a collision may be passed up
protected int NextCollisionOkTime { get; set; }
// The simulation step that last had a collision
protected long CollidingStep { get; set; }
// The simulation step that last had a collision with the ground
protected long CollidingGroundStep { get; set; }
// The collision flags we think are set in Bullet
protected CollisionFlags CurrentCollisionFlags { get; set; }
// The collisions that have been collected this tick
protected CollisionEventUpdate CollisionCollection;
// The simulation step is telling this object about a collision.
// Return 'true' if a collision was processed and should be sent up.
// Called at taint time from within the Step() function
public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
{
bool ret = false;
// The following lines make IsColliding() and IsCollidingGround() work
CollidingStep = PhysicsScene.SimulationStep;
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
{
CollidingGroundStep = PhysicsScene.SimulationStep;
}
// prims in the same linkset cannot collide with each other
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
{
return ret;
}
// if someone has subscribed for collision events....
if (SubscribedEvents()) {
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
// DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
// LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
ret = true;
}
return ret;
}
// Routine to send the collected collisions into the simulator.
// Also handles removal of this from the collection of objects with collisions if
// there are no collisions from this object. Mechanism is create one last
// collision event to make collision_end work.
// Called at taint time from within the Step() function thus no locking problems
// with CollisionCollection and ObjectsWithNoMoreCollisions.
// Return 'true' if there were some actual collisions passed up
public virtual bool SendCollisions()
{
bool ret = true;
// throttle the collisions to the number of milliseconds specified in the subscription
int nowTime = PhysicsScene.SimulationNowTime;
if (nowTime >= NextCollisionOkTime)
{
NextCollisionOkTime = nowTime + SubscribedEventsMs;
// We are called if we previously had collisions. If there are no collisions
// this time, send up one last empty event so OpenSim can sense collision end.
if (CollisionCollection.Count == 0)
{
// If I have no collisions this time, remove me from the list of objects with collisions.
ret = false;
}
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
base.SendCollisionUpdate(CollisionCollection);
// The collisionCollection structure is passed around in the simulator.
// Make sure we don't have a handle to that one and that a new one is used for next time.
CollisionCollection = new CollisionEventUpdate();
}
return ret;
}
// Subscribe for collision events.
// Parameter is the millisecond rate the caller wishes collision events to occur.
public override void SubscribeEvents(int ms) {
// DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
SubscribedEventsMs = ms;
if (ms > 0)
{
// make sure first collision happens
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
{
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
else
{
// Subscribing for zero or less is the same as unsubscribing
UnSubscribeEvents();
}
}
public override void UnSubscribeEvents() {
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
SubscribedEventsMs = 0;
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
{
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
// Return 'true' if the simulator wants collision events
public override bool SubscribedEvents() {
return (SubscribedEventsMs > 0);
}
#endregion // Collisions
// High performance detailed logging routine used by the physical objects.
protected void DetailLog(string msg, params Object[] args)
{
PhysicsScene.PhysicsLogging.Write(msg, args);
}
}
}

View File

@@ -33,7 +33,7 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
/// <summary>
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
/// This module interfaces to an unmanaged C++ library which makes the
/// actual calls into the Bullet physics engine.
/// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
@@ -62,7 +62,7 @@ public class BSPlugin : IPhysicsPlugin
if (Util.IsWindows())
Util.LoadArchSpecificWindowsDll("BulletSim.dll");
// If not Windows, loading is performed by the
// Mono loader as specified in
// Mono loader as specified in
// "bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config".
_mScene = new BSScene(sceneIdentifier);

File diff suppressed because it is too large Load Diff

View File

@@ -39,29 +39,28 @@ using log4net;
using OpenMetaverse;
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
// Test sculpties
// Move all logic out of the C++ code and into the C# code for easier future modifications.
// Test sculpties (verified that they don't work)
// Compute physics FPS reasonably
// Based on material, set density and friction
// More efficient memory usage when passing hull information from BSPrim to BulletSim
// Move all logic out of the C++ code and into the C# code for easier future modifications.
// Don't use constraints in linksets of non-physical objects. Means having to move children manually.
// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
// At the moment, physical and phantom causes object to drop through the terrain
// Physical phantom objects and related typing (collision options )
// Use collision masks for collision with terrain and phantom objects
// Check out llVolumeDetect. Must do something for that.
// Use collision masks for collision with terrain and phantom objects
// More efficient memory usage when passing hull information from BSPrim to BulletSim
// Should prim.link() and prim.delink() membership checking happen at taint time?
// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
// 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?
// Raycast
//
//
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSScene : PhysicsScene, IPhysicsParameters
@@ -74,12 +73,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public string BulletSimVersion = "?";
public Dictionary<uint, BSPhysObject> PhysObjects = new Dictionary<uint, BSPhysObject>();
public Dictionary<uint, BSPhysObject> PhysObjects;
public BSShapeCollection Shapes;
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>();
// Keeping track of the objects with collisions so we can report begin and end of a collision
public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
// Keep track of all the avatars so we can send them a collision event
// every tick so OpenSim will update its animation.
private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
// List of all the objects that have vehicle properties and should be called
// to update each physics step.
@@ -111,11 +113,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
private long m_simulationStep = 0;
public long SimulationStep { get { return m_simulationStep; } }
// The length of the last timestep we were asked to simulate.
// This is used by the vehicle code. Since the vehicle code is called
// once per simulation step, its constants need to be scaled by this.
public float LastSimulatedTimestep { get; private set; }
// A value of the time now so all the collision and update routines do not have to get their own
// Set to 'now' just before all the prims and actors are called for collisions and updates
public int SimulationNowTime { get; private set; }
@@ -132,8 +129,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 +150,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 +173,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.
@@ -203,6 +205,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override void Initialise(IMesher meshmerizer, IConfigSource config)
{
mesher = meshmerizer;
_taintedObjects = new List<TaintCallbackEntry>();
PhysObjects = new Dictionary<uint, BSPhysObject>();
Shapes = new BSShapeCollection(this);
// Allocate pinned memory to pass parameters.
m_params = new ConfigurationParameters[1];
m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
@@ -216,12 +223,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
mesher = meshmerizer;
_taintedObjects = new List<TaintCallbackEntry>();
// Enable very detailed logging.
// By creating an empty logger when not logging, the log message invocation code
// can be left in and every call doesn't have to check for null.
// can be left in and every call doesn't have to check for null.
if (m_physicsLoggingEnabled)
{
PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
@@ -252,7 +256,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// a child in a mega-region.
// Turns out that Bullet really doesn't care about the extents of the simulated
// area. It tracks active objects no matter where they are.
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f);
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
@@ -323,7 +327,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
m_log.Debug("[BULLETS UNMANAGED]:" + msg);
}
// Called directly from unmanaged code so don't do much
private void BulletLoggerPhysLog(string msg)
{
@@ -352,6 +356,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
Constraints = null;
}
if (Shapes != null)
{
Shapes.Dispose();
Shapes = null;
}
// Anything left in the unmanaged code should be cleaned out
BulletSimAPI.Shutdown(WorldID);
@@ -380,7 +390,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// TODO: Remove kludge someday.
// We must generate a collision for avatars whether they collide or not.
// This is required by OpenSim to update avatar animations, etc.
lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Add(actor);
lock (m_avatars) m_avatars.Add(actor);
return actor;
}
@@ -398,7 +408,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
// Remove kludge someday
lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Remove(bsactor);
lock (m_avatars) m_avatars.Remove(bsactor);
}
catch (Exception e)
{
@@ -450,7 +460,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
// This is a call from the simulator saying that some physical property has been updated.
// The BulletSim driver senses the changing of relevant properties so this taint
// The BulletSim driver senses the changing of relevant properties so this taint
// information call is not needed.
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
@@ -465,12 +475,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
int collidersCount = 0;
IntPtr collidersPtr;
LastSimulatedTimestep = timeStep;
int beforeTime = 0;
int simTime = 0;
// prevent simulation until we've been initialized
if (!m_initialized) return 10.0f;
int simulateStartTime = Util.EnvironmentTickCount();
if (!m_initialized) return 5.0f;
// update the prim states while we know the physics engine is not busy
int numTaints = _taintedObjects.Count;
@@ -478,23 +487,32 @@ 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
m_simulationStep++;
int numSubSteps = 0;
// Sometimes needed for debugging to find out what happened before the step
// PhysicsLogging.Flush();
try
{
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
DetailLog("{0},Simulate,call, nTaints= {1}, substeps={2}, updates={3}, colliders={4}",
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}",
DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
}
catch (Exception e)
{
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
updatedEntityCount = 0;
collidersCount = 0;
@@ -521,18 +539,34 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
// This is a kludge to get avatar movement updates.
// the simulator expects collisions for avatars even if there are have been no collisions. This updates
// avatar animations and stuff.
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
foreach (BSPhysObject bsp in m_avatars)
bsp.SendCollisions();
// The above SendCollision's batch up the collisions on the objects.
// Now push the collisions into the simulator.
foreach (BSPhysObject bsp in m_objectsWithCollisions)
bsp.SendCollisions();
m_objectsWithCollisions.Clear();
if (ObjectsWithCollisions.Count > 0)
{
foreach (BSPhysObject bsp in ObjectsWithCollisions)
if (!m_avatars.Contains(bsp)) // don't call avatars twice
if (!bsp.SendCollisions())
{
// If the object is done colliding, see that it's removed from the colliding list
ObjectsWithNoMoreCollisions.Add(bsp);
}
}
// 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();
// Objects that are done colliding are removed from the ObjectsWithCollisions list.
// Not done above because it is inside an iteration of ObjectWithCollisions.
if (ObjectsWithNoMoreCollisions.Count > 0)
{
foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
ObjectsWithCollisions.Remove(po);
ObjectsWithNoMoreCollisions.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 +578,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
{
pobj.UpdateProperties(entprop);
continue;
}
}
}
@@ -558,18 +591,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
// this is a waste since the outside routine also calcuates the physics simulation
// period. TODO: There should be a way of computing physics frames from simulator computation.
// long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
// return (timeStep * (float)simulateTotalTime);
// TODO: FIX THIS: fps calculation possibly wrong.
// This calculation says 1/timeStep is the ideal frame rate. Any time added to
// that by the physics simulation gives a slower frame rate.
long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
if (totalSimulationTime >= timeStep)
return 0;
return 1f / (timeStep + totalSimulationTime);
// The physics engine returns the number of milliseconds it simulated this call.
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
// Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
return numSubSteps * m_fixedTimeStep * 1000;
}
// Something has collided
@@ -580,28 +605,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 collider;
if (!PhysObjects.TryGetValue(localID, out collider))
{
// If the object that is colliding cannot be found, just ignore the collision.
DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
return;
}
// 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);
ActorTypes type = ActorTypes.Prim;
if (collidingWith <= TerrainManager.HighestTerrainID)
DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
{
type = ActorTypes.Ground;
// If a collision was posted, remember to send it to the simulator
ObjectsWithCollisions.Add(collider);
}
else
{
collidee = PhysObjects[collidingWith];
if (collidee is BSCharacter)
type = ActorTypes.Agent;
}
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration);
m_objectsWithCollisions.Add(collider);
return;
}
@@ -616,17 +638,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
TerrainManager.SetTerrain(heightMap);
}
public override void SetWaterLevel(float baseheight)
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;
}
public override void DeleteTerrain()
public override void DeleteTerrain()
{
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
}
@@ -659,121 +681,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 +689,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return;
lock (_taintLock)
{
_taintedObjects.Add(new TaintCallbackEntry(ident, callback));
}
return;
}
@@ -820,13 +730,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
{
if (newType == Vehicle.TYPE_NONE)
RemoveVehiclePrim(vehic);
if (newType != Vehicle.TYPE_NONE)
{
RemoveVehiclePrim(vehic);
}
else
{
// make it so the scene will call us each tick to do vehicle things
// make it so the scene will call us each tick to do vehicle things
AddVehiclePrim(vehic);
}
}
@@ -858,7 +765,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
// Some prims have extra vehicle actions
// no locking because only called when physics engine is not busy
// Called at taint time!
private void ProcessVehicles(float timeStep)
{
foreach (BSPhysObject pobj in m_vehicles)
@@ -900,7 +807,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// getters and setters.
// It is easiest to find an existing definition and copy it.
// Parameter values are floats. Booleans are converted to a floating value.
//
//
// A ParameterDefn() takes the following parameters:
// -- the text name of the parameter. This is used for console input and ini file.
// -- a short text description of the parameter. This shows up in the console listing.
@@ -919,14 +826,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,
@@ -1102,12 +1009,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
0f, // zero to disable
0f,
(s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].maxPersistantManifoldPoolSize; },
(s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ),
new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
0f, // zero to disable
0f,
(s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; },
(s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ),
@@ -1122,12 +1029,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].shouldForceUpdateAllAabbs; },
(s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ),
new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
ConfigurationParameters.numericFalse,
ConfigurationParameters.numericTrue,
(s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
(s) => { return s.m_params[0].shouldRandomizeSolverOrder; },
(s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ),
new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
ConfigurationParameters.numericFalse,
ConfigurationParameters.numericTrue,
(s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
(s) => { return s.m_params[0].shouldSplitSimulationIslands; },
(s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ),
@@ -1162,8 +1069,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 +1079,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,
@@ -1241,7 +1153,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
if (SettableParameters.Length < ParameterDefinitions.Length)
{
List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
{
@@ -1317,7 +1228,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
});
break;
default:
default:
// setting only one localID
TaintedUpdateParameter(parm, localID, val);
break;

View File

@@ -0,0 +1,737 @@
/*
* 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 copyrightD
* 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.Text;
using OMV = OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;
using OpenSim.Region.Physics.ConvexDecompositionDotNet;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSShapeCollection : IDisposable
{
// private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
protected BSScene PhysicsScene { get; set; }
private Object m_collectionActivityLock = new Object();
// Description of a Mesh
private struct MeshDesc
{
public IntPtr ptr;
public int referenceCount;
public DateTime lastReferenced;
}
// Description of a hull.
// Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects
private struct HullDesc
{
public IntPtr ptr;
public int referenceCount;
public DateTime lastReferenced;
}
private struct BodyDesc
{
public IntPtr ptr;
// Bodies are only used once so reference count is always either one or zero
public int referenceCount;
public DateTime lastReferenced;
}
private Dictionary<ulong, MeshDesc> Meshes = new Dictionary<ulong, MeshDesc>();
private Dictionary<ulong, HullDesc> Hulls = new Dictionary<ulong, HullDesc>();
private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
public BSShapeCollection(BSScene physScene)
{
PhysicsScene = physScene;
}
public void Dispose()
{
// TODO!!!!!!!!!
}
// Callbacks called just before either the body or shape is destroyed.
// Mostly used for changing bodies out from under Linksets.
// Useful for other cases where parameters need saving.
// Passing 'null' says no callback.
public delegate void ShapeDestructionCallback(BulletShape shape);
public delegate void BodyDestructionCallback(BulletBody body);
// Called to update/change the body and shape for an object.
// First checks the shape and updates that if necessary then makes
// sure the body is of the right type.
// Return 'true' if either the body or the shape changed.
// Called at taint-time!!
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{
bool ret = false;
// This lock could probably be pushed down lower but building shouldn't take long
lock (m_collectionActivityLock)
{
// Do we have the correct geometry for this type of object?
// Updates prim.BSShape with information/pointers to requested shape
bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
// If we had to select a new shape geometry for the object,
// rebuild the body around it.
// Updates prim.BSBody with information/pointers to requested body
bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
prim.BSShape, shapeData, bodyCallback);
ret = newGeom || newBody;
}
DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}",
prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape);
return ret;
}
// Track another user of a body
// We presume the caller has allocated the body.
// Bodies only have one user so the reference count is either 1 or 0.
public void ReferenceBody(BulletBody body, bool atTaintTime)
{
lock (m_collectionActivityLock)
{
BodyDesc bodyDesc;
if (Bodies.TryGetValue(body.ID, out bodyDesc))
{
bodyDesc.referenceCount++;
DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount);
}
else
{
// New entry
bodyDesc.ptr = body.ptr;
bodyDesc.referenceCount = 1;
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={1}", body.ID, body, bodyDesc.referenceCount);
}
bodyDesc.lastReferenced = System.DateTime.Now;
Bodies[body.ID] = bodyDesc;
}
}
// Release the usage of a body.
// Called when releasing use of a BSBody. BSShape is handled separately.
public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
{
if (body.ptr == IntPtr.Zero)
return;
lock (m_collectionActivityLock)
{
BodyDesc bodyDesc;
if (Bodies.TryGetValue(body.ID, out bodyDesc))
{
bodyDesc.referenceCount--;
bodyDesc.lastReferenced = System.DateTime.Now;
Bodies[body.ID] = bodyDesc;
DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);
// If body is no longer being used, free it -- bodies are never shared.
if (bodyDesc.referenceCount == 0)
{
Bodies.Remove(body.ID);
BSScene.TaintCallback removeOperation = delegate()
{
DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}",
body.ID, body.ptr.ToString("X"));
// If the caller needs to know the old body is going away, pass the event up.
if (bodyCallback != null) bodyCallback(body);
// Zero any reference to the shape so it is not freed when the body is deleted.
BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
// It may have already been removed from the world in which case the next is a NOOP.
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
};
// If already in taint-time, do the operations now. Otherwise queue for later.
if (inTaintTime)
removeOperation();
else
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
}
}
else
{
DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
}
}
}
// Track the datastructures and use count for a shape.
// When creating a hull, this is called first to reference the mesh
// and then again to reference the hull.
// Meshes and hulls for the same shape have the same hash key.
// NOTE that native shapes are not added to the mesh list or removed.
// Returns 'true' if this is the initial reference to the shape. Otherwise reused.
private bool ReferenceShape(BulletShape shape)
{
bool ret = false;
switch (shape.type)
{
case ShapeData.PhysicsShapeType.SHAPE_MESH:
MeshDesc meshDesc;
if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
{
// There is an existing instance of this mesh.
meshDesc.referenceCount++;
DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
}
else
{
// This is a new reference to a mesh
meshDesc.ptr = shape.ptr;
// We keep a reference to the underlying IMesh data so a hull can be built
meshDesc.referenceCount = 1;
DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
ret = true;
}
meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc;
break;
case ShapeData.PhysicsShapeType.SHAPE_HULL:
HullDesc hullDesc;
if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
{
// There is an existing instance of this hull.
hullDesc.referenceCount++;
DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
}
else
{
// This is a new reference to a hull
hullDesc.ptr = shape.ptr;
hullDesc.referenceCount = 1;
DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
ret = true;
}
hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc;
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
// Native shapes are not tracked and they don't go into any list
break;
}
return ret;
}
// Release the usage of a shape.
// The collisionObject is released since it is a copy of the real collision shape.
public void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback)
{
if (shape.ptr == IntPtr.Zero)
return;
BSScene.TaintCallback dereferenceOperation = delegate()
{
switch (shape.type)
{
case ShapeData.PhysicsShapeType.SHAPE_HULL:
DereferenceHull(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
// Native shapes are not tracked and are released immediately
if (shape.ptr != IntPtr.Zero & shape.isNativeShape)
{
DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime);
if (shapeCallback != null) shapeCallback(shape);
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
}
break;
}
};
if (atTaintTime)
{
lock (m_collectionActivityLock)
{
dereferenceOperation();
}
}
else
{
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceShape", dereferenceOperation);
}
}
// Count down the reference count for a mesh shape
// Called at taint-time.
private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
{
MeshDesc meshDesc;
if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
{
meshDesc.referenceCount--;
// TODO: release the Bullet storage
if (shapeCallback != null) shapeCallback(shape);
meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc;
DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
}
}
// Count down the reference count for a hull shape
// Called at taint-time.
private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
{
HullDesc hullDesc;
if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
{
hullDesc.referenceCount--;
// TODO: release the Bullet storage (aging old entries?)
if (shapeCallback != null) shapeCallback(shape);
hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc;
DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
}
}
// 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, BSPrim prim, ShapeData shapeData,
PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
{
bool ret = false;
bool haveShape = false;
bool nativeShapePossible = true;
// If the prim attributes are simple, this could be a simple Bullet native shape
if (nativeShapePossible
&& ((pbs.SculptEntry && !PhysicsScene.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)
{
haveShape = true;
if (forceRebuild
|| prim.Scale != shapeData.Size
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE
)
{
ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE,
ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.BSShape);
}
}
else
{
haveShape = true;
if (forceRebuild
|| prim.Scale != shapeData.Size
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX
)
{
ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX,
ShapeData.FixedShapeKey.KEY_BOX, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.BSShape);
}
}
}
// If a simple shape is not happening, create a mesh and possibly a hull.
// Note that if it's a native shape, the check for physical/non-physical is not
// made. Native shapes are best used in either case.
if (!haveShape)
{
if (prim.IsPhysical)
{
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
}
else
{
ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
}
}
return ret;
}
// Creates a native shape and assignes it to prim.BSShape
private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape;
shapeData.Type = shapeType;
// Bullet native objects are scaled by the Bullet engine so pass the size in
prim.Scale = shapeData.Size;
shapeData.Scale = shapeData.Size;
// release any previous shape
DereferenceShape(prim.BSShape, true, shapeCallback);
// Native shapes are always built independently.
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
newShape.shapeKey = (ulong)shapeKey;
newShape.isNativeShape = true;
// Don't need to do a 'ReferenceShape()' here because native shapes are not tracked.
// DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape);
prim.BSShape = newShape;
return true;
}
// Builds a mesh shape in the physical world and updates prim.BSShape.
// Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise .
// Called at taint-time!
private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape = new BulletShape(IntPtr.Zero);
float lod;
ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod);
// if this new shape is the same as last time, don't recreate the mesh
if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
return false;
DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}",
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
// Since we're recreating new, get rid of the reference to the previous shape
DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
ReferenceShape(newShape);
// meshes are already scaled by the meshmerizer
prim.Scale = new OMV.Vector3(1f, 1f, 1f);
prim.BSShape = newShape;
return true; // 'true' means a new shape has been added to this prim
}
private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{
IMesh meshData = null;
IntPtr meshPtr;
MeshDesc meshDesc;
if (Meshes.TryGetValue(newMeshKey, out meshDesc))
{
// If the mesh has already been built just use it.
meshPtr = meshDesc.ptr;
}
else
{
// Pass false for physicalness as this creates some sort of bounding box which we don't need
meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
int[] indices = meshData.getIndexListAsInt();
List<OMV.Vector3> vertices = meshData.getVertexList();
float[] verticesAsFloats = new float[vertices.Count * 3];
int vi = 0;
foreach (OMV.Vector3 vv in vertices)
{
verticesAsFloats[vi++] = vv.X;
verticesAsFloats[vi++] = vv.Y;
verticesAsFloats[vi++] = vv.Z;
}
// m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
// LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
}
BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey;
return newShape;
}
// See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever.
private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape;
float lod;
ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod);
// if the hull hasn't changed, don't rebuild it
if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
return false;
DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}",
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
// Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull.
DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
ReferenceShape(newShape);
// hulls are already scaled by the meshmerizer
prim.Scale = new OMV.Vector3(1f, 1f, 1f);
prim.BSShape = newShape;
return true; // 'true' means a new shape has been added to this prim
}
List<ConvexResult> m_hulls;
private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{
IntPtr hullPtr;
HullDesc hullDesc;
if (Hulls.TryGetValue(newHullKey, out hullDesc))
{
// If the hull shape already is created, just use it.
hullPtr = hullDesc.ptr;
}
else
{
// Build a new hull in the physical world
// Pass false for physicalness as this creates some sort of bounding box which we don't need
IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
int[] indices = meshData.getIndexListAsInt();
List<OMV.Vector3> vertices = meshData.getVertexList();
//format conversion from IMesh format to DecompDesc format
List<int> convIndices = new List<int>();
List<float3> convVertices = new List<float3>();
for (int ii = 0; ii < indices.GetLength(0); ii++)
{
convIndices.Add(indices[ii]);
}
foreach (OMV.Vector3 vv in vertices)
{
convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
}
// setup and do convex hull conversion
m_hulls = new List<ConvexResult>();
DecompDesc dcomp = new DecompDesc();
dcomp.mIndices = convIndices;
dcomp.mVertices = convVertices;
ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
// create the hull into the _hulls variable
convexBuilder.process(dcomp);
// Convert the vertices and indices for passing to unmanaged.
// The hull information is passed as a large floating point array.
// The format is:
// convHulls[0] = number of hulls
// convHulls[1] = number of vertices in first hull
// convHulls[2] = hull centroid X coordinate
// convHulls[3] = hull centroid Y coordinate
// convHulls[4] = hull centroid Z coordinate
// convHulls[5] = first hull vertex X
// convHulls[6] = first hull vertex Y
// convHulls[7] = first hull vertex Z
// convHulls[8] = second hull vertex X
// ...
// convHulls[n] = number of vertices in second hull
// convHulls[n+1] = second hull centroid X coordinate
// ...
//
// TODO: is is very inefficient. Someday change the convex hull generator to return
// data structures that do not need to be converted in order to pass to Bullet.
// And maybe put the values directly into pinned memory rather than marshaling.
int hullCount = m_hulls.Count;
int totalVertices = 1; // include one for the count of the hulls
foreach (ConvexResult cr in m_hulls)
{
totalVertices += 4; // add four for the vertex count and centroid
totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
}
float[] convHulls = new float[totalVertices];
convHulls[0] = (float)hullCount;
int jj = 1;
foreach (ConvexResult cr in m_hulls)
{
// copy vertices for index access
float3[] verts = new float3[cr.HullVertices.Count];
int kk = 0;
foreach (float3 ff in cr.HullVertices)
{
verts[kk++] = ff;
}
// add to the array one hull's worth of data
convHulls[jj++] = cr.HullIndices.Count;
convHulls[jj++] = 0f; // centroid x,y,z
convHulls[jj++] = 0f;
convHulls[jj++] = 0f;
foreach (int ind in cr.HullIndices)
{
convHulls[jj++] = verts[ind].x;
convHulls[jj++] = verts[ind].y;
convHulls[jj++] = verts[ind].z;
}
}
// create the hull data structure in Bullet
hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
}
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
newShape.shapeKey = newHullKey;
return newShape; // 'true' means a new shape has been added to this prim
}
// Callback from convex hull creater with a newly created hull.
// Just add it to our collection of hulls for this shape.
private void HullReturn(ConvexResult result)
{
m_hulls.Add(result);
return;
}
// Create a hash of all the shape parameters to be used as a key
// for this particular shape.
private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod)
{
// level of detail based on size and type of the object
float lod = PhysicsScene.MeshLOD;
if (pbs.SculptEntry)
lod = PhysicsScene.SculptLOD;
float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z));
if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
lod = PhysicsScene.MeshMegaPrimLOD;
retLod = lod;
return (ulong)pbs.GetMeshKey(shapeData.Size, lod);
}
// For those who don't want the LOD
private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs)
{
float lod;
return ComputeShapeKey(shapeData, pbs, out lod);
}
// Create a body object in Bullet.
// Updates prim.BSBody with the information about the new body if one is created.
// Returns 'true' if an object was actually created.
// Called at taint-time.
private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape,
ShapeData shapeData, BodyDestructionCallback bodyCallback)
{
bool ret = false;
// the mesh, hull or native shape must have already been created in Bullet
bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero);
// If there is an existing body, verify it's of an acceptable type.
// If not a solid object, body is a GhostObject. Otherwise a RigidBody.
if (!mustRebuild)
{
CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr);
if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
|| !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
{
// If the collisionObject is not the correct type for solidness, rebuild what's there
mustRebuild = true;
}
}
if (mustRebuild || forceRebuild)
{
DereferenceBody(prim.BSBody, true, bodyCallback);
BulletBody aBody;
IntPtr bodyPtr = IntPtr.Zero;
if (prim.IsSolid)
{
bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation);
// DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
}
else
{
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation);
// DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
}
aBody = new BulletBody(shapeData.ID, bodyPtr);
ReferenceBody(aBody, true);
prim.BSBody = aBody;
ret = true;
}
return ret;
}
private void DetailLog(string msg, params Object[] args)
{
PhysicsScene.PhysicsLogging.Write(msg, args);
}
}
}

View File

@@ -57,14 +57,14 @@ public class BSTerrainManager
public const float TERRAIN_COLLISION_MARGIN = 0.0f;
// Until the whole simulator is changed to pass us the region size, we rely on constants.
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, 0f);
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// The scene that I am part of
private BSScene m_physicsScene;
private BSScene PhysicsScene { get; set; }
// The ground plane created to keep thing from falling to infinity.
private BulletBody m_groundPlane;
// If doing mega-regions, if we're region zero we will be managing multiple
// region terrains since region zero does the physics for the whole mega-region.
private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps;
@@ -84,18 +84,18 @@ public class BSTerrainManager
// If the parent region (region 0), this is the extent of the combined regions
// relative to the origin of region zero
private Vector3 m_worldMax;
private PhysicsScene m_parentScene;
private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
public BSTerrainManager(BSScene physicsScene)
{
m_physicsScene = physicsScene;
PhysicsScene = physicsScene;
m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>();
m_terrainModified = false;
// Assume one region of default size
m_worldOffset = Vector3.Zero;
m_worldMax = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, 4096f);
m_parentScene = null;
m_worldMax = new Vector3(DefaultRegionSize);
MegaRegionParentPhysicsScene = null;
}
// Create the initial instance of terrain and the underlying ground plane.
@@ -107,10 +107,16 @@ public class BSTerrainManager
public void CreateInitialGroundPlaneAndTerrain()
{
// The ground plane is here to catch things that are trying to drop to negative infinity
BulletShape groundPlaneShape = new BulletShape(BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN));
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.Ptr, Vector3.Zero, Quaternion.Identity));
BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
BulletShape groundPlaneShape = new BulletShape(
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN),
ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE);
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
Vector3.Zero, Quaternion.Identity));
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
// Everything collides with the ground plane.
BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
(uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE);
Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION);
@@ -126,13 +132,13 @@ public class BSTerrainManager
// Release all the terrain structures we might have allocated
public void ReleaseGroundPlaneAndTerrain()
{
if (m_groundPlane.Ptr != IntPtr.Zero)
if (m_groundPlane.ptr != IntPtr.Zero)
{
if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr))
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
{
BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
}
m_groundPlane.Ptr = IntPtr.Zero;
m_groundPlane.ptr = IntPtr.Zero;
}
ReleaseTerrain();
@@ -143,9 +149,9 @@ public class BSTerrainManager
{
foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps)
{
if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr))
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr))
{
BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr);
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
}
}
@@ -154,27 +160,32 @@ 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;
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 && MegaRegionParentPhysicsScene != 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 (MegaRegionParentPhysicsScene is BSScene)
{
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
BSScene.DetailLogZero, m_worldOffset, m_worldMax);
((BSScene)MegaRegionParentPhysicsScene).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
@@ -190,10 +201,10 @@ public class BSTerrainManager
// The 'doNow' boolean says whether to do all the unmanaged activities right now (like when
// calling this routine from initialization or taint-time routines) or whether to delay
// all the unmanaged activities to taint-time.
private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool doNow)
private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime)
{
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},doNow={3}",
BSScene.DetailLogZero, minCoords, maxCoords, doNow);
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}",
BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime);
float minZ = float.MaxValue;
float maxZ = float.MinValue;
@@ -223,12 +234,12 @@ public class BSTerrainManager
mapInfo.maxZ = maxZ;
mapInfo.sizeX = maxCoords.X - minCoords.X;
mapInfo.sizeY = maxCoords.Y - minCoords.Y;
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
BSScene.TaintCallback rebuildOperation = delegate()
{
if (m_parentScene != null)
if (MegaRegionParentPhysicsScene != null)
{
// It's possible that Combine() was called after this code was queued.
// If we are a child of combined regions, we don't create any terrain for us.
@@ -241,17 +252,17 @@ public class BSTerrainManager
return;
}
if (mapInfo.terrainBody.Ptr != IntPtr.Zero)
if (mapInfo.terrainBody.ptr != IntPtr.Zero)
{
// Updating an existing terrain.
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
// Remove from the dynamics world because we're going to mangle this object
BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr);
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// Get rid of the old terrain
BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr);
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
mapInfo.Ptr = IntPtr.Zero;
@@ -278,11 +289,11 @@ public class BSTerrainManager
// else
{
// Creating a new terrain.
DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
mapInfo.ID = id;
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.ID,
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// The terrain object initial position is at the center of the object
@@ -292,41 +303,49 @@ public class BSTerrainManager
centerPos.Z = minZ + ((maxZ - minZ) / 2f);
// Create the terrain shape from the mapInfo
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
mapInfo.terrainBody = new BulletBody(mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.Ptr,
centerPos, Quaternion.Identity));
mapInfo.terrainBody = new BulletBody(mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
id, centerPos, Quaternion.Identity));
}
// Make sure the entry is in the heightmap table
m_heightMaps[terrainRegionBase] = mapInfo;
// Set current terrain attributes
BulletSimAPI.SetFriction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
BulletSimAPI.SetMassProps2(mapInfo.terrainBody.Ptr, 0f, Vector3.Zero);
BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.Ptr);
BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero);
BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr);
// Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr);
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
(uint)CollisionFilterGroups.TerrainFilter,
(uint)CollisionFilterGroups.TerrainMask);
// Make sure the new shape is processed.
BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true);
// BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true;
};
// There is the option to do the changes now (we're already in 'taint time'), or
// to do the Bullet operations later.
if (doNow)
if (atTaintTime)
rebuildOperation();
else
m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
}
else
{
@@ -342,7 +361,7 @@ public class BSTerrainManager
Vector3 minCoordsX = minCoords;
Vector3 maxCoordsX = maxCoords;
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
// Code that must happen at taint-time
@@ -351,19 +370,21 @@ public class BSTerrainManager
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y);
// Create a new mapInfo that will be filled with the new info
mapInfo = new BulletHeightMapInfo(id, heightMapX,
BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, newTerrainID,
BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID,
minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
// Put the unfilled heightmap info into the collection of same
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.
if (doNow)
if (atTaintTime)
createOperation();
else
m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
}
}
@@ -383,7 +404,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 +424,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.
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);
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
LogHeader, PhysicsScene.RegionName, tX, tY);
}
m_terrainModified = false;
lastHeight = ret;
return ret;
}
@@ -425,7 +454,7 @@ public class BSTerrainManager
{
return true;
}
// This routine is called two ways:
// One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
// extent of the combined regions. This is to inform the parent of the size
@@ -437,14 +466,14 @@ public class BSTerrainManager
{
m_worldOffset = offset;
m_worldMax = extents;
m_parentScene = pScene;
MegaRegionParentPhysicsScene = pScene;
if (pScene != null)
{
// We are a child.
// We are a child.
// We want m_worldMax to be the highest coordinate of our piece of terrain.
m_worldMax = offset + DefaultRegionSize;
}
DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
}
@@ -458,7 +487,7 @@ public class BSTerrainManager
private void DetailLog(string msg, params Object[] args)
{
m_physicsScene.PhysicsLogging.Write(msg, args);
PhysicsScene.PhysicsLogging.Write(msg, args);
}
}
}

View File

@@ -38,35 +38,112 @@ namespace OpenSim.Region.Physics.BulletSPlugin {
// The physics engine controller class created at initialization
public struct BulletSim
{
public BulletSim(uint worldId, BSScene bss, IntPtr xx) { worldID = worldId; scene = bss; Ptr = xx; }
public BulletSim(uint worldId, BSScene bss, IntPtr xx)
{
ptr = xx;
worldID = worldId;
physicsScene = bss;
}
public IntPtr ptr;
public uint worldID;
// The scene is only in here so very low level routines have a handle to print debug/error messages
public BSScene scene;
public IntPtr Ptr;
}
public struct BulletShape
{
public BulletShape(IntPtr xx) { Ptr = xx; }
public IntPtr Ptr;
public BSScene physicsScene;
}
// An allocated Bullet btRigidBody
public struct BulletBody
{
public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; }
public IntPtr Ptr;
public BulletBody(uint id, IntPtr xx)
{
ID = id;
ptr = xx;
collisionFilter = 0;
collisionMask = 0;
}
public IntPtr ptr;
public uint ID;
public CollisionFilterGroups collisionFilter;
public CollisionFilterGroups collisionMask;
public override string ToString()
{
StringBuilder buff = new StringBuilder();
buff.Append("<id=");
buff.Append(ID.ToString());
buff.Append(",p=");
buff.Append(ptr.ToString("X"));
if (collisionFilter != 0 || collisionMask != 0)
{
buff.Append(",f=");
buff.Append(collisionFilter.ToString("X"));
buff.Append(",m=");
buff.Append(collisionMask.ToString("X"));
}
buff.Append(">");
return buff.ToString();
}
}
public struct BulletShape
{
public BulletShape(IntPtr xx)
{
ptr = xx;
type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN;
shapeKey = 0;
isNativeShape = false;
}
public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ)
{
ptr = xx;
type = typ;
shapeKey = 0;
isNativeShape = false;
}
public IntPtr ptr;
public ShapeData.PhysicsShapeType type;
public ulong shapeKey;
public bool isNativeShape;
// Hulls have an underlying mesh. A pointer to it is hidden here.
public override string ToString()
{
StringBuilder buff = new StringBuilder();
buff.Append("<p=");
buff.Append(ptr.ToString("X"));
buff.Append(",s=");
buff.Append(type.ToString());
buff.Append(",k=");
buff.Append(shapeKey.ToString("X"));
buff.Append(",n=");
buff.Append(isNativeShape.ToString());
buff.Append(">");
return buff.ToString();
}
}
// Constraint type values as defined by Bullet
public enum ConstraintType : int
{
POINT2POINT_CONSTRAINT_TYPE = 3,
HINGE_CONSTRAINT_TYPE,
CONETWIST_CONSTRAINT_TYPE,
D6_CONSTRAINT_TYPE,
SLIDER_CONSTRAINT_TYPE,
CONTACT_CONSTRAINT_TYPE,
D6_SPRING_CONSTRAINT_TYPE,
MAX_CONSTRAINT_TYPE
}
// An allocated Bullet btConstraint
public struct BulletConstraint
{
public BulletConstraint(IntPtr xx) { Ptr = xx; }
public IntPtr Ptr;
public BulletConstraint(IntPtr xx)
{
ptr = xx;
}
public IntPtr ptr;
}
// An allocated HeightMapThing which hold various heightmap info
// An allocated HeightMapThing which holds various heightmap info.
// Made a class rather than a struct so there would be only one
// instance of this and C# will pass around pointers rather
// than making copies.
@@ -96,14 +173,14 @@ public class BulletHeightMapInfo
// ===============================================================================
[StructLayout(LayoutKind.Sequential)]
public struct ConvexHull
public struct ConvexHull
{
Vector3 Offset;
int VertexCount;
Vector3[] Vertices;
}
[StructLayout(LayoutKind.Sequential)]
public struct ShapeData
public struct ShapeData
{
public enum PhysicsShapeType
{
@@ -114,7 +191,9 @@ public struct ShapeData
SHAPE_CYLINDER = 4,
SHAPE_SPHERE = 5,
SHAPE_MESH = 6,
SHAPE_HULL = 7
SHAPE_HULL = 7,
SHAPE_GROUNDPLANE = 8,
SHAPE_TERRAIN = 9,
};
public uint ID;
public PhysicsShapeType Type;
@@ -130,13 +209,24 @@ public struct ShapeData
public float Restitution;
public float Collidable; // true of things bump into this
public float Static; // true if a static object. Otherwise gravity, etc.
public float Solid; // true if object cannot be passed through
public Vector3 Size;
// note that bools are passed as floats since bool size changes by language and architecture
public const float numericTrue = 1f;
public const float numericFalse = 0f;
// The native shapes have predefined shape hash keys
public enum FixedShapeKey : ulong
{
KEY_BOX = 1,
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SweepHit
public struct SweepHit
{
public uint ID;
public float Fraction;
@@ -213,12 +303,36 @@ 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,
}
public enum CollisionObjectTypes : int
{
CO_COLLISION_OBJECT = 1 << 0,
CO_RIGID_BODY = 1 << 1,
CO_GHOST_OBJECT = 1 << 2,
CO_SOFT_BODY = 1 << 3,
CO_HF_FLUID = 1 << 4,
CO_USER_TYPE = 1 << 5,
}
// 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,
@@ -230,11 +344,55 @@ public enum CollisionFlags : uint
CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
// Following used by BulletSim to control collisions
BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
BS_VOLUME_DETECT_OBJECT = 1 << 11,
BS_PHANTOM_OBJECT = 1 << 12,
BS_PHYSICAL_OBJECT = 1 << 13,
BS_FLOATS_ON_WATER = 1 << 11,
BS_NONE = 0,
BS_ALL = 0xFFFFFFFF,
// These are the collision flags switched depending on physical state.
// The other flags are used for other things and should not be fooled with.
BS_ACTIVE = CF_STATIC_OBJECT
| CF_KINEMATIC_OBJECT
| CF_NO_CONTACT_RESPONSE
};
// Values for collisions groups and masks
public enum CollisionFilterGroups : uint
{
// Don't use the bit definitions!! Define the use in a
// filter/mask definition below. This way collision interactions
// are more easily debugged.
BNoneFilter = 0,
BDefaultFilter = 1 << 0,
BStaticFilter = 1 << 1,
BKinematicFilter = 1 << 2,
BDebrisFilter = 1 << 3,
BSensorTrigger = 1 << 4,
BCharacterFilter = 1 << 5,
BAllFilter = 0xFFFFFFFF,
// Filter groups defined by BulletSim
BGroundPlaneFilter = 1 << 10,
BTerrainFilter = 1 << 11,
BRaycastFilter = 1 << 12,
BSolidFilter = 1 << 13,
// The collsion filters and masked are defined in one place -- don't want them scattered
AvatarFilter = BCharacterFilter,
AvatarMask = BAllFilter,
ObjectFilter = BSolidFilter,
ObjectMask = BAllFilter,
StaticObjectFilter = BStaticFilter,
StaticObjectMask = BAllFilter,
VolumeDetectFilter = BSensorTrigger,
VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter,
TerrainMask = BAllFilter & ~BStaticFilter,
GroundPlaneFilter = BAllFilter,
GroundPlaneMask = BAllFilter
};
// 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
@@ -269,8 +427,8 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg
public static extern string GetVersion();
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
int maxUpdates, IntPtr updateArray,
DebugLogCallback logRoutine);
@@ -289,19 +447,19 @@ public static extern bool UpdateParameter(uint worldID, uint localID,
// ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount,
public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount,
out IntPtr updatedEntitiesPtr,
out int collidersCount,
out IntPtr collidersPtr);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateHull(uint worldID, System.UInt64 meshKey,
public static extern bool CreateHull(uint worldID, System.UInt64 meshKey,
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls
);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey,
public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey,
int indexCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices
);
@@ -315,23 +473,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 +488,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 +545,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 +556,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,
@@ -429,7 +573,7 @@ public static extern void Shutdown2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int PhysicsStep2(IntPtr world, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount,
out int updatedEntityCount,
out IntPtr updatedEntitiesPtr,
out int collidersCount,
out IntPtr collidersPtr);
@@ -438,9 +582,10 @@ 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,
public static extern IntPtr CreateMeshShape2(IntPtr world,
int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices );
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -448,30 +593,61 @@ public static extern IntPtr CreateHullShape2(IntPtr world,
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildHullShape2(IntPtr world, IntPtr meshShape);
public static extern IntPtr BuildHullShapeFromMesh2(IntPtr world, IntPtr meshShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildNativeShape2(IntPtr world,
float shapeType, float collisionMargin, Vector3 scale);
public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData);
[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 DuplicateCollisionShape2(IntPtr sim, IntPtr srcShape, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, uint id, IntPtr constructionInfo);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot);
public static extern int GetBodyType2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, uint id, 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 CreateBodyWithDefaultMotionState2(IntPtr shape, uint id, Vector3 pos, Quaternion rot);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateGhostFromShape2(IntPtr sim, IntPtr shape, uint id, Vector3 pos, Quaternion rot);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
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 IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr FillHeightMapInfo2(IntPtr sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords,
public static extern IntPtr FillHeightMapInfo2(IntPtr sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords,
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -484,6 +660,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,
@@ -508,7 +685,7 @@ public static extern void SetConstraintEnable2(IntPtr constrain, float numericTr
public static extern void SetConstraintNumSolverIterations2(IntPtr constrain, float iterations);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetFrames2(IntPtr constrain,
public static extern bool SetFrames2(IntPtr constrain,
Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -536,16 +713,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,88 +822,293 @@ 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);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask);
// =====================================================================================
// 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);
// =====================================================================================
// Debugging
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpPhysicsStatistics2(IntPtr sim);

View File

@@ -63,6 +63,9 @@ namespace OpenSim.Region.Physics.OdePlugin
private bool m_isphysical;
public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } }
private int m_expectedCollisionContacts = 0;
/// <summary>
/// Is this prim subject to physics? Even if not, it's still solid for collision purposes.
/// </summary>
@@ -150,7 +153,7 @@ namespace OpenSim.Region.Physics.OdePlugin
private PrimitiveBaseShape _pbs;
private OdeScene _parent_scene;
/// <summary>
/// The physics space which contains prim geometries
/// </summary>
@@ -840,7 +843,7 @@ namespace OpenSim.Region.Physics.OdePlugin
int vertexStride, triStride;
mesh.getVertexListAsPtrToFloatArray(out vertices, out vertexStride, out vertexCount); // Note, that vertices are fixed in unmanaged heap
mesh.getIndexListAsPtrToIntArray(out indices, out triStride, out indexCount); // Also fixed, needs release after usage
m_expectedCollisionContacts = indexCount;
mesh.releaseSourceMeshData(); // free up the original mesh data to save memory
// We must lock here since m_MeshToTriMeshMap is static and multiple scene threads may call this method at
@@ -1377,6 +1380,7 @@ Console.WriteLine("CreateGeom:");
{
//Console.WriteLine(" CreateGeom 1");
SetGeom(d.CreateSphere(m_targetSpace, _size.X / 2));
m_expectedCollisionContacts = 3;
}
catch (AccessViolationException)
{
@@ -1391,6 +1395,7 @@ Console.WriteLine("CreateGeom:");
{
//Console.WriteLine(" CreateGeom 2");
SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
m_expectedCollisionContacts = 4;
}
catch (AccessViolationException)
{
@@ -1406,6 +1411,7 @@ Console.WriteLine("CreateGeom:");
{
//Console.WriteLine(" CreateGeom 3");
SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
m_expectedCollisionContacts = 4;
}
catch (AccessViolationException)
{
@@ -1421,6 +1427,7 @@ Console.WriteLine("CreateGeom:");
{
//Console.WriteLine(" CreateGeom 4");
SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
m_expectedCollisionContacts = 4;
}
catch (AccessViolationException)
{
@@ -1446,11 +1453,13 @@ Console.WriteLine("CreateGeom:");
_parent_scene.geom_name_map.Remove(prim_geom);
_parent_scene.actor_name_map.Remove(prim_geom);
d.GeomDestroy(prim_geom);
m_expectedCollisionContacts = 0;
prim_geom = IntPtr.Zero;
}
catch (System.AccessViolationException)
{
prim_geom = IntPtr.Zero;
m_expectedCollisionContacts = 0;
m_log.ErrorFormat("[PHYSICS]: PrimGeom dead for {0}", Name);
return false;
@@ -2395,15 +2404,15 @@ Console.WriteLine(" JointCreateFixed");
{
get
{
// Averate previous velocity with the new one so
// Average previous velocity with the new one so
// client object interpolation works a 'little' better
if (_zeroFlag)
return Vector3.Zero;
Vector3 returnVelocity = Vector3.Zero;
returnVelocity.X = (m_lastVelocity.X + _velocity.X)/2;
returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y)/2;
returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z)/2;
returnVelocity.X = (m_lastVelocity.X + _velocity.X) * 0.5f; // 0.5f is mathematically equiv to '/ 2'
returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y) * 0.5f;
returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z) * 0.5f;
return returnVelocity;
}
set
@@ -2600,6 +2609,7 @@ Console.WriteLine(" JointCreateFixed");
{
Vector3 pv = Vector3.Zero;
bool lastZeroFlag = _zeroFlag;
float m_minvelocity = 0;
if (Body != (IntPtr)0) // FIXME -> or if it is a joint
{
d.Vector3 vec = d.BodyGetPosition(Body);
@@ -2752,8 +2762,21 @@ Console.WriteLine(" JointCreateFixed");
_acceleration = ((_velocity - m_lastVelocity) / 0.1f);
_acceleration = new Vector3(_velocity.X - m_lastVelocity.X / 0.1f, _velocity.Y - m_lastVelocity.Y / 0.1f, _velocity.Z - m_lastVelocity.Z / 0.1f);
//m_log.Info("[PHYSICS]: V1: " + _velocity + " V2: " + m_lastVelocity + " Acceleration: " + _acceleration.ToString());
// Note here that linearvelocity is affecting angular velocity... so I'm guessing this is a vehicle specific thing...
// it does make sense to do this for tiny little instabilities with physical prim, however 0.5m/frame is fairly large.
// reducing this to 0.02m/frame seems to help the angular rubberbanding quite a bit, however, to make sure it doesn't affect elevators and vehicles
// adding these logical exclusion situations to maintain this where I think it was intended to be.
if (m_throttleUpdates || m_usePID || (m_vehicle != null && m_vehicle.Type != Vehicle.TYPE_NONE) || (Amotor != IntPtr.Zero))
{
m_minvelocity = 0.5f;
}
else
{
m_minvelocity = 0.02f;
}
if (_velocity.ApproxEquals(pv, 0.5f))
if (_velocity.ApproxEquals(pv, m_minvelocity))
{
m_rotationalVelocity = pv;
}

View File

@@ -336,6 +336,7 @@ namespace OpenSim.Region.Physics.OdePlugin
public int geomContactPointsStartthrottle = 3;
public int geomUpdatesPerThrottledUpdate = 15;
private const int avatarExpectedContacts = 3;
public float bodyPIDD = 35f;
public float bodyPIDG = 25;
@@ -474,6 +475,8 @@ namespace OpenSim.Region.Physics.OdePlugin
private OdePrim cp1;
private OdeCharacter cc2;
private OdePrim cp2;
private int p1ExpectedPoints = 0;
private int p2ExpectedPoints = 0;
//private int cStartStop = 0;
//private string cDictKey = "";
@@ -498,6 +501,7 @@ namespace OpenSim.Region.Physics.OdePlugin
public int physics_logging_interval = 0;
public bool physics_logging_append_existing_logfile = false;
public d.Vector3 xyz = new d.Vector3(128.1640f, 128.3079f, 25.7600f);
public d.Vector3 hpr = new d.Vector3(125.5000f, -17.0000f, 0.0000f);
@@ -644,7 +648,7 @@ namespace OpenSim.Region.Physics.OdePlugin
contactsPerCollision = physicsconfig.GetInt("contacts_per_collision", 80);
geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 3);
geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 5);
geomUpdatesPerThrottledUpdate = physicsconfig.GetInt("geom_updates_before_throttled_update", 15);
geomCrossingFailuresBeforeOutofbounds = physicsconfig.GetInt("geom_crossing_failures_before_outofbounds", 5);
@@ -1064,7 +1068,10 @@ namespace OpenSim.Region.Physics.OdePlugin
PhysicsActor p1;
PhysicsActor p2;
p1ExpectedPoints = 0;
p2ExpectedPoints = 0;
if (!actor_name_map.TryGetValue(g1, out p1))
{
p1 = PANull;
@@ -1121,9 +1128,13 @@ namespace OpenSim.Region.Physics.OdePlugin
switch (p1.PhysicsActorType)
{
case (int)ActorTypes.Agent:
p1ExpectedPoints = avatarExpectedContacts;
p2.CollidingObj = true;
break;
case (int)ActorTypes.Prim:
if (p1 != null && p1 is OdePrim)
p1ExpectedPoints = ((OdePrim) p1).ExpectedCollisionContacts;
if (p2.Velocity.LengthSquared() > 0.0f)
p2.CollidingObj = true;
break;
@@ -1319,6 +1330,7 @@ namespace OpenSim.Region.Physics.OdePlugin
if ((p2.PhysicsActorType == (int) ActorTypes.Agent) &&
(Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
{
p2ExpectedPoints = avatarExpectedContacts;
// Avatar is moving on terrain, use the movement terrain contact
AvatarMovementTerrainContact.geom = curContact;
@@ -1332,6 +1344,7 @@ namespace OpenSim.Region.Physics.OdePlugin
{
if (p2.PhysicsActorType == (int)ActorTypes.Agent)
{
p2ExpectedPoints = avatarExpectedContacts;
// Avatar is standing on terrain, use the non moving terrain contact
TerrainContact.geom = curContact;
@@ -1356,9 +1369,18 @@ namespace OpenSim.Region.Physics.OdePlugin
}
if (p2 is OdePrim)
material = ((OdePrim)p2).m_material;
{
material = ((OdePrim) p2).m_material;
p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
}
// Unnessesary because p1 is defined above
//if (p1 is OdePrim)
// {
// p1ExpectedPoints = ((OdePrim)p1).ExpectedCollisionContacts;
// }
//m_log.DebugFormat("Material: {0}", material);
m_materialContacts[material, movintYN].geom = curContact;
if (m_global_contactcount < maxContactsbeforedeath)
@@ -1379,7 +1401,10 @@ namespace OpenSim.Region.Physics.OdePlugin
int material = (int)Material.Wood;
if (p2 is OdePrim)
{
material = ((OdePrim)p2).m_material;
p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
}
//m_log.DebugFormat("Material: {0}", material);
m_materialContacts[material, movintYN].geom = curContact;
@@ -1429,6 +1454,7 @@ namespace OpenSim.Region.Physics.OdePlugin
{
if ((p2.PhysicsActorType == (int)ActorTypes.Agent))
{
p2ExpectedPoints = avatarExpectedContacts;
if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
{
// Avatar is moving on a prim, use the Movement prim contact
@@ -1458,7 +1484,10 @@ namespace OpenSim.Region.Physics.OdePlugin
int material = (int)Material.Wood;
if (p2 is OdePrim)
{
material = ((OdePrim)p2).m_material;
p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
}
//m_log.DebugFormat("Material: {0}", material);
m_materialContacts[material, 0].geom = curContact;
@@ -1479,8 +1508,8 @@ namespace OpenSim.Region.Physics.OdePlugin
}
collision_accounting_events(p1, p2, maxDepthContact);
if (count > geomContactPointsStartthrottle)
if (count > ((p1ExpectedPoints + p2ExpectedPoints) * 0.25) + (geomContactPointsStartthrottle))
{
// If there are more then 3 contact points, it's likely
// that we've got a pile of objects, so ...

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

Some files were not shown because too many files have changed in this diff Show More