Compare commits

...

223 Commits

Author SHA1 Message Date
Justin Clark-Casey (justincc)
6e2ffd7050 minor: move recent OnAgentUpdate/OnPreAgentUpdate event doc up into IClientAPI from LLClientView 2012-11-28 02:47:40 +00:00
Justin Clark-Casey (justincc)
8726748e22 Reuse the same AgentUpdateArgs object for each AgentUpdate UDP packet (of which there are 10 a second) rather than constructing a new one every time.
We can do this because AgentUpdate packets are handled synchronously.
2012-11-28 02:47:31 +00:00
Justin Clark-Casey (justincc)
73c2db9e8f Explicitly return only the incoming AgentUpdate packet as this is the only one we pool atm, rather than attempting to return all incoming packets. 2012-11-28 02:47:24 +00:00
Justin Clark-Casey (justincc)
c1cab3e752 If RecycleBaseUDPPackets = true, also pool IncomingPackets to reduce memory churn 2012-11-28 02:47:15 +00:00
Justin Clark-Casey (justincc)
713fdda7f8 minor: Make BasicCircuitTests.SetUp() call overriden base method instead of ignoring it. 2012-11-28 02:47:08 +00:00
Justin Clark-Casey (justincc)
baa599e5b0 Add LastMemoryChurn stat using existing data so we can more quickly tell how memory churn changes rather than waiting for the average to move. 2012-11-28 02:47:01 +00:00
Justin Clark-Casey (justincc)
014c533ebe Add optional pool for the UDPPacketBuffer objects that handle all incoming UDP data.
Even when an avatar is standing still, it's sending in a constant stream of AgentUpdate packets that the client creates new UDPPacketBuffer objects to handle.
This option pools those objects.  This reduces memory churn.
Currently off by default.  Works but the scope can be expanded.
2012-11-28 02:46:53 +00:00
Justin Clark-Casey (justincc)
53d87510ce Make it possible to separate start and stop lludp packet processing from the console for debug processes.
This is controlled via the "debug lludp start <in|out|all>" and "debug lludp stop <in|out|all>" region console commands.
The command "debug lludp status" will show current status.
2012-11-28 02:46:46 +00:00
Justin Clark-Casey (justincc)
43aa74d139 minor: Comment out log message on every FRIENDS SIM CONNECTOR request for now. 2012-11-28 02:46:39 +00:00
Justin Clark-Casey (justincc)
ada9238907 minor: Add missing newlines and spacing to help for "debug eq" console command 2012-11-28 02:46:23 +00:00
Justin Clark-Casey (justincc)
4d98cdf829 minor: Add/correct some doc messages associated with entity teleport.
I believe UseCircuitCode is sent on EnableSimulator EQ message, rather than EstablishAgentCommunication
At least with LL 3.3.4, EstablishAgentCommunication appears unnecessary in the teleport context -
viewer still requests it though possibly only after TeleportFinish().  However, we will continue to send it.
2012-11-28 02:46:16 +00:00
Justin Clark-Casey (justincc)
547043047e minor: comment out "Registered seed capability" message for "Received SEED caps request" message for now.
I think this is more useful right now since it tells us if the viewer requested a seed caps at all in various scenarios (such as when teleporting to a new region).
2012-11-28 02:46:10 +00:00
Justin Clark-Casey (justincc)
9d3b68411a minor: Fix and elaborate on log information printed when an unrecognized estate method is received from the client. 2012-11-28 02:45:11 +00:00
Justin Clark-Casey (justincc)
470f8d3c04 Get rid of accidental scene start left in ScenePresenceAutopilotTests 2012-11-28 02:44:53 +00:00
Justin Clark-Casey (justincc)
da00b57d4b Fix problems with regression tests by allowing invoke of Update() with a finite number of frames even if the scene isn't active. 2012-11-28 02:44:45 +00:00
Justin Clark-Casey (justincc)
caedac67e0 Get Watchdog to log thread removal 2012-11-28 02:44:38 +00:00
Justin Clark-Casey (justincc)
2b5dc4eba4 Add "active true|false" to "debug scene" console command.
This allows the scene update and maintenance loops to be started and stopped for debug purposes.
2012-11-28 02:44:30 +00:00
Justin Clark-Casey (justincc)
39b8d01e71 minor: Use && instead of & when deciding whether to print Improve/ObjectUpdate packet out messages when debug is turned on.
Practical effect is probably none.
2012-11-28 02:44:23 +00:00
Justin Clark-Casey (justincc)
fca3154982 minor: Fix bug in categorization of blocks reused stat from packetpool 2012-11-28 02:44:17 +00:00
Justin Clark-Casey (justincc)
cef158c42d Remove now unnecessary OpenSim.Framework.Console reference from Monitoring.
Properly resolve circular reference which no longer appeared in Mono build as there were no using statements for it.
2012-11-28 02:44:10 +00:00
Justin Clark-Casey (justincc)
177c3bcfe6 Add AgentUpdate to PacketPool. This is the most common inbound packet from viewers. 2012-11-28 02:44:02 +00:00
Justin Clark-Casey (justincc)
35766f2c3a minor: Comment out "Client requested range for texture ... but" message. This is not useful during normal operation. 2012-11-28 02:43:55 +00:00
Justin Clark-Casey (justincc)
e175cf543c Fix percentage stats to multiply by 100. Adjust container name for packetpool stats. 2012-11-28 02:43:48 +00:00
Justin Clark-Casey (justincc)
96b0d1276e Enable reuse of data blocks for ImprovedTerseObjectUpdate using existing Packetpool code. 2012-11-28 02:43:42 +00:00
Justin Clark-Casey (justincc)
301546289e Fix build break by moving OpenSim.Framework.Console back below HttpServer in the build order.
Luckily, it turns out Framework.Monitoring doesn't need to reference Console directly.
2012-11-28 02:43:32 +00:00
Justin Clark-Casey (justincc)
813778b39e Fix packetpool for ImprovedTerseObjectUpdate packets.
These were neither being returned or in many places reused.
Getting packets from a pool rather than deallocating and reallocating reduces memory churn which in turn reduces garbage collection time and frequency.
2012-11-28 02:43:23 +00:00
Justin Clark-Casey (justincc)
6c3eceb197 Extend "show stats" command to "show stats [list|all|<category name>]"
This allows different categories of stats to be shown, with options to list categories or show all stats.
Currently categories are scene and simulator and only a very few stats are currently registered via this mechanism.
This commit also adds percentage stats for packets and blocks reused from the packet pool.
2012-11-28 02:43:11 +00:00
Justin Clark-Casey (justincc)
0afc43eed7 Assign endVector before control leaves ObjectCommandsModule.TryParseVectorRange() in order to fix mono 2.4.3 compile failure.
This doesn't fail the compile on mono 2.10.8.
2012-11-28 02:42:56 +00:00
Justin Clark-Casey (justincc)
6eb260d4eb Add "delete object pos <start-coord> to <end-coord>" console command.
This allows one to delete objects within a certain volume.
See help on console for more details.
2012-11-28 02:42:44 +00:00
Justin Clark-Casey (justincc)
63f1efc414 minor: Add method doc to EventManager.OnNewClient, OnClientLogin, ClientClosed, OnRemovePresence that any long-running operations should be launched on a thread separate to the event thread. 2012-11-28 02:42:02 +00:00
Justin Clark-Casey (justincc)
e685f7ab68 Lock on AgentCircuitData during Scene.AddClient() and RemoveClient() to prevent an inactive connection being left behind if the user closes the viewer whilst the connection is being established.
This should remove the need to run the console command "kick user --force" when these connections are left around.
2012-11-28 02:41:55 +00:00
Justin Clark-Casey (justincc)
ac8f420adb minor: elaborate method doc on Scene.NewUserConnection() 2012-11-28 02:41:42 +00:00
SignpostMarv
fa16f132e3 string format arguments in wrong order 2012-11-28 02:36:16 +00:00
Justin Clark-Casey (justincc)
7a5782c3e3 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-11-28 02:35:28 +00:00
Justin Clark-Casey (justincc)
cf98691a3d Merge branch '0.7.4-post-fixes' into 0.7.4-extended
Conflicts:
	OpenSim/Data/MySQL/MySQLXInventoryData.cs
2012-11-15 02:52:37 +00:00
Justin Clark-Casey (justincc)
09aa87ba26 Fix SceneObjectUndoRedoTests.TestNoUndoOnObjectsNotInScene by not looking up ParentGroup scale limits if part is not in a sog 2012-11-15 02:38:57 +00:00
Justin Clark-Casey (justincc)
38fb0430bf Replace old Prebuild.exe accidentally added back in f977291 with one built without <copy> nant target generation and on mono 2.4.3 2012-11-15 02:16:03 +00:00
Justin Clark-Casey (justincc)
ec82e2fde8 Replace previously updated Prebuild.exe with one built directly with mono 2.4.3 2012-11-15 02:15:53 +00:00
Justin Clark-Casey (justincc)
fba2864cbc Stop Prebuild from generating <copy> statements which unnecessarily copy files into bin/Debug or bin/Release
nant_0.91~alpha2+dfsg-3_all.deb in Ubuntu 12.04 and earlier actually ignored these due to a bug
However, nant 0.92~rc1+dfsg-2 in Ubuntu 12.10 fixes this bug (possibly https://github.com/nant/nant/pull/39).
Which makes nant time-consumingly copy these files when the aren't actually used.
Tested removal of <copy> on both nant 0.91 and nant 0.92
Will be submitting this patch to prebuild project for comment though I suspect there's nobody there to pay attention.
2012-11-15 02:15:28 +00:00
Justin Clark-Casey (justincc)
60c6d3e108 Add [AssetService] AllowRemoteDeleteAllTypes (default false).
This allows a closed grid to delete asset types other than maptile remotely.
Only operational if AllowRemoteDelete = true also.
Defaults to false - do not enable if anybody other than you can make asset service requests.
2012-11-15 02:09:31 +00:00
Justin Clark-Casey (justincc)
92f9750ded Move check to allow only deletion of maptiles up to AssetServerDeleteHandler from AssetService.
This allows us to use a common check for both AssetService and XAssetService.
It also allows future console commands to delete an asset.
As before, deletion of maptile assets is not allowed remotely unless this is explicitly configured.
2012-11-15 02:09:20 +00:00
Justin Clark-Casey (justincc)
23106bce76 Merge branch '0.7.4-post-fixes' into 0.7.4-extended 2012-11-14 02:51:44 +00:00
Justin Clark-Casey (justincc)
15acba1972 minor: Adjust method doc on IXInventoryData MoveItem() and MoveFolder() to make it clear we're moving to an existing folder. 2012-11-14 01:53:32 +00:00
Justin Clark-Casey (justincc)
909d4dca82 Update parent inventory folder version numbers when folders are moved/created/deleted to match version numbers cached by viewers.
This is done in the way that one would expect (e.g. moving a folder increments version number on both source and destination parent folders).
This should hopefully improve viewer reuse of its cached inventory information.
Currently MySQL only but will be implement for SQLite/MSSQL if there are no issues.
2012-11-14 01:53:23 +00:00
Justin Clark-Casey (justincc)
5676f1d037 Update folder version numbers when moving items and making the Delete(string[], string[]) call (not just string, string).
This is to stop viewer inventory cache version numbers becoming out of sync with grid stored numbers when viewer performs these actions.
If there are no problems with these changes, they will be propogated to SQLite (and MSSQL if that's simple enough).
May also need to do the same on folder store/create/delete and maybe propogate version increments up the folder hierarchy, but that requires investigation.
2012-11-14 01:52:53 +00:00
Justin Clark-Casey (justincc)
d864d76254 Increment version number of a folder when an object it contains is deleted.
Not doing this was allowing the viewer inventory cache to become out of sync if an item was directly deleted.
2012-11-14 01:52:44 +00:00
Justin Clark-Casey (justincc)
9ff4d38cc9 Update folder version numbers when moving items and making the Delete(string[], string[]) call (not just string, string).
This is to stop viewer inventory cache version numbers becoming out of sync with grid stored numbers when viewer performs these actions.
If there are no problems with these changes, they will be propogated to SQLite (and MSSQL if that's simple enough).
May also need to do the same on folder store/create/delete and maybe propogate version increments up the folder hierarchy, but that requires investigation.
2012-11-08 21:16:49 +00:00
Justin Clark-Casey (justincc)
8dadbf706d Increment version number of a folder when an object it contains is deleted.
Not doing this was allowing the viewer inventory cache to become out of sync if an item was directly deleted.
2012-11-08 21:16:42 +00:00
Justin Clark-Casey (justincc)
ff968cbe43 refactor: Rename UserSessioNID -> UserSession in WebStatsModule since this is what it actually represents 2012-10-11 00:22:37 +01:00
Justin Clark-Casey (justincc)
582540657f Make UserSessionID a class rather than a struct, so that later updates to value suceed (rather than having to pull the data out and reinsert back into the Dictionary).
Fixes http://opensimulator.org/mantis/view.php?id=6338
2012-10-11 00:22:30 +01:00
Justin Clark-Casey (justincc)
05eeee6ee8 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-10-11 00:22:23 +01:00
Justin Clark-Casey (justincc)
cd6f3b147d Don't store undo states if a scene object is manipulated when it is not in a scene.
Adds regression test for this.
2012-10-11 00:22:16 +01:00
Justin Clark-Casey (justincc)
86f519ba57 refactor: Change control structures in SOP.StoreUndoState() to reduce nesting. 2012-10-11 00:22:10 +01:00
Justin Clark-Casey (justincc)
febc6bae30 Make it possible to rescale SOGs when they are not in a scene. 2012-10-11 00:22:03 +01:00
Justin Clark-Casey (justincc)
48f818bf07 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-10-11 00:21:56 +01:00
Justin Clark-Casey (justincc)
c2d21bb8cc Add basic undo/redo regression tests. 2012-10-11 00:21:49 +01:00
Justin Clark-Casey (justincc)
39fe1ba028 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-10-11 00:21:39 +01:00
Justin Clark-Casey (justincc)
7112e860dc 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-10-11 00:21:30 +01:00
Justin Clark-Casey (justincc)
4606137882 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-10-11 00:21:22 +01:00
Justin Clark-Casey (justincc)
8eab6b7701 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-10-11 00:21:16 +01:00
Justin Clark-Casey (justincc)
71cd68eec1 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-10-11 00:21:08 +01:00
Justin Clark-Casey (justincc)
168c3b78f4 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-10-11 00:21:01 +01:00
Justin Clark-Casey (justincc)
d175e49e15 refactor: Rename UserSessioNID -> UserSession in WebStatsModule since this is what it actually represents 2012-10-10 23:55:51 +01:00
Justin Clark-Casey (justincc)
79624d762e Make UserSessionID a class rather than a struct, so that later updates to value suceed (rather than having to pull the data out and reinsert back into the Dictionary).
Fixes http://opensimulator.org/mantis/view.php?id=6338
2012-10-10 23:55:39 +01:00
Justin Clark-Casey (justincc)
3ec9eec257 Fix bug in implementation of "show part pos" that would not filter probably.
Also refactors more of ObjectCommandsModule to remove duplicate code
2012-10-10 23:55:18 +01:00
Justin Clark-Casey (justincc)
e5ac4a72b7 Add "show part pos" console command to match "show object pos" 2012-10-10 23:55:12 +01:00
Justin Clark-Casey (justincc)
470ea6cf70 refactor: eliminate some now duplicate code in ObjectCommandsModule 2012-10-10 23:55:06 +01:00
Justin Clark-Casey (justincc)
45dc7ac7d1 Add Vector3.Zero return on TryParseConsoleVector() which fails on mono 2.4.3 but not mono 2.10.9 2012-10-10 23:54:58 +01:00
Justin Clark-Casey (justincc)
2905eaa8ff Add missing ConsoleUtil from last commit 2012-10-10 23:54:51 +01:00
Justin Clark-Casey (justincc)
deb068050b Add "show object pos <start-coord> to <end-coord>" command to simulator console.
This allows you to display details of all objects in a given bounding box.
Values parts of the co-ord can be left out as appropriate (e.g. to get all objects between the ground and z=30.
See "help show object pos" for more details.
2012-10-10 23:54:44 +01:00
Justin Clark-Casey (justincc)
8f803c5a7d Finally fix build break by allowing UserManagementModule (which constructs a packet directly) to reference OpenSim.Region.ClientStack.LindenUDP.
This time I actually properly did a clean build
2012-10-10 23:54:36 +01:00
Justin Clark-Casey (justincc)
601c7998eb Print number of objects found with "show object name" and "show part name" simulator console commands 2012-10-10 23:54:27 +01:00
Justin Clark-Casey (justincc)
5a686d40de Forgot to actually remove the packetpool set code from scene. 2012-10-10 23:54:19 +01:00
Justin Clark-Casey (justincc)
57837a1e81 Read PacketPool config in LLUDPServer with other config params rather than in Scene.
This is to resolve previous build break.
This unnecessarily but harmlessly reads and sets the parameter multiple times - scene was doing the same thing.
2012-10-10 23:54:13 +01:00
Justin Clark-Casey (justincc)
5162ebd2cc refactor: Move OpenSim.Framework.PacketPool to OpenSim.Region.Clientstack.Linden.UDP
This is to allow it to use OpenSim.Framework.Monitoring in the future.
This is also a better location since the packet pool is linden udp specific
2012-10-10 23:54:04 +01:00
Justin Clark-Casey (justincc)
1f869ab36d Output monitor data in response to console command on MainConsole.Instance rather than m_log
This should really be happening for all console commands (though many don't).
However, things might get difficult if both a console command and other code invoke the same paths.
2012-10-10 23:53:29 +01:00
Justin Clark-Casey (justincc)
e912e52e15 Add generic PercentageStat.
Not yet used.
2012-10-10 23:53:21 +01:00
Justin Clark-Casey (justincc)
14b0c03d5b 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-10 23:52:59 +01:00
Justin Clark-Casey (justincc)
8414ec9429 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-10-10 23:51:20 +01:00
Justin Clark-Casey (justincc)
ac03b1b82f 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-10-10 23:49:30 +01:00
Justin Clark-Casey (justincc)
d5c999553e Don't store undo states if a scene object is manipulated when it is not in a scene.
Adds regression test for this.
2012-10-10 23:47:34 +01:00
Justin Clark-Casey (justincc)
371df42d3f refactor: Change control structures in SOP.StoreUndoState() to reduce nesting. 2012-10-10 23:47:15 +01:00
Justin Clark-Casey (justincc)
a4a0396850 Make it possible to rescale SOGs when they are not in a scene. 2012-10-10 23:46:58 +01:00
Justin Clark-Casey (justincc)
83ad75b997 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-10-10 23:39:14 +01:00
Justin Clark-Casey (justincc)
3f45d4ba9d Add basic undo/redo regression tests. 2012-10-10 23:39:02 +01:00
Justin Clark-Casey (justincc)
2fa8bc201a 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-10-10 23:38:39 +01:00
Justin Clark-Casey (justincc)
e6b99ec849 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-10-10 23:38:26 +01:00
Justin Clark-Casey (justincc)
e5c665384c 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-10-10 23:37:59 +01:00
Justin Clark-Casey (justincc)
5bb1273b3d 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-10-10 23:37:52 +01:00
Justin Clark-Casey (justincc)
5629f5141e 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-10-10 23:37:29 +01:00
SignpostMarv
15aef01b34 Documenting object-related events 2012-10-10 23:37:14 +01:00
SignpostMarv
67a010298f Documenting LSL script-related events 2012-10-10 23:37:05 +01:00
SignpostMarv
7c398a532b Documenting non-LSL script-related events 2012-10-10 23:36:43 +01:00
Justin Clark-Casey (justincc)
eefd39a0d5 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-10-10 23:36:10 +01:00
Justin Clark-Casey (justincc)
176b1c85c0 minor: Make slow outgoing request log messages consistent with other log messages 2012-10-10 23:35:45 +01:00
Justin Clark-Casey (justincc)
2da6cfde80 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-10-10 23:35:35 +01:00
Justin Clark-Casey (justincc)
8068c083f6 Simplify UuidGatherer by performing asset fetch synchronously rather than using the async call but waiting for completion anyway! 2012-10-10 23:35:27 +01:00
Justin Clark-Casey (justincc)
bbe04aab95 Lock GDI+ portion og VectorRenderModule.GetDrawStringSize() to prevent concurrent thread use provoking mono crashes.
Same rationale as commit 13690582.
2012-09-28 02:38:51 +01:00
Justin Clark-Casey (justincc)
d31a951d94 Lock GDI+ portion og VectorRenderModule.GetDrawStringSize() to prevent concurrent thread use provoking mono crashes.
Same rationale as commit 13690582.
2012-09-28 02:37:50 +01:00
Justin Clark-Casey (justincc)
c27a158961 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-28 00:12:54 +01:00
SignpostMarv
068e97cf0e Documentation of teleport-related events 2012-09-28 00:12:48 +01:00
SignpostMarv
aa45d831f3 minor tweaks to existing comments for IDE goodness 2012-09-28 00:12:40 +01:00
SignpostMarv
eff8448154 Documentation of agent-related events 2012-09-28 00:12:32 +01:00
SignpostMarv
badd7f1578 Documentation of object-related events 2012-09-28 00:12:24 +01:00
SignpostMarv
e12cb7bca9 Documentation of OnPluginConsole 2012-09-28 00:12:17 +01:00
Justin Clark-Casey (justincc)
23fe2a2103 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-27 23:55:15 +01:00
Justin Clark-Casey (justincc)
e684e426af Add openmetaverse_data from libopenmetaverse to allow testing of texture baking via bot rather than just throwing out errors 2012-09-27 23:54:59 +01:00
Justin Clark-Casey (justincc)
e65959b6f7 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-27 23:54:40 +01:00
Justin Clark-Casey (justincc)
8041af6d18 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-27 23:54:32 +01:00
SignpostMarv
56873b319b Documentation of parcel-related events
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:54:13 +01:00
SignpostMarv
b315fab771 correcting a typo that causes c# express to complain about xml comment containing invalid xml
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:54:05 +01:00
Justin Clark-Casey (justincc)
05342ed677 minor: Comment out friends notification log spam for now. 2012-09-27 23:53:47 +01:00
SignpostMarv
487bf31e06 documentation (OnSceneObjectPartCopy) 2012-09-27 23:53:20 +01:00
SignpostMarv
6f3b2ea632 documentation (OnRemovePresence) 2012-09-27 23:53:12 +01:00
SignpostMarv
307fdeff78 documentation (OnNewPresence) 2012-09-27 23:53:00 +01:00
SignpostMarv
8df3edcc30 documentation (OnClientConnect) 2012-09-27 23:52:51 +01:00
SignpostMarv
a43e282efa documentation (OnBackup) 2012-09-27 23:52:34 +01:00
SignpostMarv
7374690957 documentation (OnTerrainTick) 2012-09-27 23:52:26 +01:00
SignpostMarv
0c69575670 documentation (OnTerrainTainted) 2012-09-27 23:52:19 +01:00
SignpostMarv
148cad1976 documentation (OnClientMovement) 2012-09-27 23:52:08 +01:00
SignpostMarv
7db4e00b14 Documenting some of the events on OpenSim.Region.Framework.Scenes.EventManager (OnFrame) 2012-09-27 23:51:15 +01:00
BlueWall
a00f745eba Add file to .gitignore
Add OpenSim.userprefs which is created by Monodevelop to .gitignore
2012-09-27 23:50:53 +01:00
SignpostMarv
9ae335fafc adding documentation to script invokation methods 2012-09-27 23:49:06 +01:00
Justin Clark-Casey (justincc)
6bd7639d66 Also do other MySQL region settings related calls under m_dbLock, in common with other calls. 2012-09-27 23:48:51 +01:00
Justin Clark-Casey (justincc)
c336309234 Do Windlight storage and removal calls in MySQL under m_dbLock, as is done with all the other database calls. 2012-09-27 23:48:45 +01:00
SignpostMarv
41d217321d fixing bug where last element in list is ignored 2012-09-27 23:48:27 +01:00
Justin Clark-Casey (justincc)
2226dd1efb Revert "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"
This reverts commit f8612b0d1b.
2012-09-27 23:29:47 +01:00
Justin Clark-Casey (justincc)
44699b07e4 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-27 23:24:40 +01:00
Justin Clark-Casey (justincc)
6739f0752b 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-27 23:24:32 +01:00
Justin Clark-Casey (justincc)
1ebde81d6a 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-27 23:24:22 +01:00
Justin Clark-Casey (justincc)
195625b5e7 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-27 23:24:14 +01:00
Justin Clark-Casey (justincc)
951c2702ff 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-27 23:24:08 +01:00
SignpostMarv
02b90df6ff Documentation of teleport-related events 2012-09-27 23:24:01 +01:00
SignpostMarv
f7c01c8e90 minor tweaks to existing comments for IDE goodness 2012-09-27 23:23:55 +01:00
SignpostMarv
e8c964efaf Documentation of agent-related events 2012-09-27 23:23:49 +01:00
SignpostMarv
74baab6f0f Documentation of object-related events 2012-09-27 23:23:39 +01:00
SignpostMarv
bff0ea2fc3 Documentation of OnPluginConsole 2012-09-27 23:23:33 +01:00
Justin Clark-Casey (justincc)
e474c19c40 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-27 23:23:27 +01:00
Justin Clark-Casey (justincc)
a2a81f1bb7 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-27 23:23:18 +01:00
Justin Clark-Casey (justincc)
c291284629 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-27 23:23:11 +01:00
Justin Clark-Casey (justincc)
7f7ad71def Add openmetaverse_data from libopenmetaverse to allow testing of texture baking via bot rather than just throwing out errors 2012-09-27 23:23:05 +01:00
justincc
7dfc0f30cc 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-27 23:22:57 +01:00
Justin Clark-Casey (justincc)
c14e67a8bb 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-27 23:21:19 +01:00
Justin Clark-Casey (justincc)
75b7e6009a 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-27 23:21:12 +01:00
Justin Clark-Casey (justincc)
63df56d613 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-27 23:20:42 +01:00
SignpostMarv
f2e1f3e659 Documentation of parcel-related events
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:20:28 +01:00
SignpostMarv
b1b7d3b004 correcting a typo that causes c# express to complain about xml comment containing invalid xml
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:20:21 +01:00
Justin Clark-Casey (justincc)
f2c12d4fba Fix usage statement on "debug http" console command since max level is now 5 rather than 3 2012-09-27 23:20:10 +01:00
Justin Clark-Casey (justincc)
3a60c9c8c8 Fix bug in logging sample input at debug http level 4.
Also converts newlines to "\n" text.
2012-09-27 23:20:04 +01:00
Justin Clark-Casey (justincc)
44cd864205 minor: Comment out friends notification log spam for now. 2012-09-27 23:19:53 +01:00
SignpostMarv
f70d7013a5 Documentation of economy-related EventManager events 2012-09-27 23:19:41 +01:00
Mic Bowman
ac1e902d59 Allow an incoming identifier to be specified for a JsonStore. 2012-09-27 23:18:37 +01:00
Justin Clark-Casey (justincc)
b67bdee3cf Make "show http-handlers" command available for ROBUST instances as well as the simulator executable. 2012-09-27 23:18:08 +01:00
Justin Clark-Casey (justincc)
83d4fa98f0 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-27 23:17:59 +01:00
SignpostMarv
70a968b342 documentation (OnSceneObjectPartCopy) 2012-09-27 23:17:53 +01:00
SignpostMarv
8e576f7511 documentation (OnRemovePresence) 2012-09-27 23:17:44 +01:00
SignpostMarv
4526424436 documentation (OnNewPresence) 2012-09-27 23:17:38 +01:00
SignpostMarv
fd43620e87 documentation (OnClientConnect) 2012-09-27 23:17:32 +01:00
SignpostMarv
56b8d331cb documentation (OnBackup) 2012-09-27 23:17:24 +01:00
SignpostMarv
14bfba8b3b documentation (OnTerrainTick) 2012-09-27 23:17:17 +01:00
SignpostMarv
af8d3ae790 documentation (OnTerrainTainted) 2012-09-27 23:17:11 +01:00
SignpostMarv
e6bb7e99be documentation (OnClientMovement) 2012-09-27 23:17:05 +01:00
SignpostMarv
97c56adb9d Documenting some of the events on OpenSim.Region.Framework.Scenes.EventManager (OnFrame) 2012-09-27 23:16:59 +01:00
SignpostMarv
f8612b0d1b 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-27 23:16:35 +01:00
SignpostMarv
a2a46a18ae 4096 is used in various places as the maximum height of a region, refactoring to be a constant 2012-09-27 23:16:25 +01:00
Justin Clark-Casey (justincc)
1cf888b71f Add missing DynamicTexture.cs file from last commit 2012-09-27 23:15:49 +01:00
Justin Clark-Casey (justincc)
0109603cdc 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-27 23:15:43 +01:00
SignpostMarv
c7c750d127 adding utility method for getting SceneObjectGroup from scene
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:15:30 +01:00
SignpostMarv
b738b50cd6 adding utility method for getting SceneObjectPart from scene
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:15:23 +01:00
SignpostMarv
351daf90f1 pasting in show uptime code
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-27 23:15:07 +01:00
BlueWall
19d9acf63a Add file to .gitignore
Add OpenSim.userprefs which is created by Monodevelop to .gitignore
2012-09-27 23:14:52 +01:00
Justin Clark-Casey (justincc)
eb40d3b6fc 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-27 23:14:39 +01:00
BlueWall
f5da23d9db Fix Windows build
Add reference to fix Windows build: no windows here to test, please report any issues back to IRC #opensim-dev ASAP
2012-09-27 23:13:56 +01:00
SignpostMarv
b412bce095 adding documentation to script invokation methods 2012-09-27 23:13:11 +01:00
Justin Clark-Casey (justincc)
0b57ddd753 Also do other MySQL region settings related calls under m_dbLock, in common with other calls. 2012-09-27 23:12:58 +01:00
Justin Clark-Casey (justincc)
d0a6d82a23 Do Windlight storage and removal calls in MySQL under m_dbLock, as is done with all the other database calls. 2012-09-27 23:12:48 +01:00
Justin Clark-Casey (justincc)
ba2792bd1f Make ReuseDynamicTextures an experimental config setting in [Textures]. Default is false, as before.
If true, this setting reuses dynamically generated textures (i.e. created through osSetDynamicTextureData() and similar OSSL functions) where possible rather than always regenerating them.
This results in much quicker updates viewer-side but may bloat the asset cache (though this is fixable).
Also, sometimes issue have been seen where dynamic textures do not transfer to the viewer properly (permanently blurry).
If this happens and that flag is set then they are not regenerated, the viewer has to clear cache or wait for 24 hours before all cached uuids are invalidated.
CUrrently experimental.  Default is false, as before.
2012-09-27 23:12:39 +01:00
Justin Clark-Casey (justincc)
3f8c09e006 If the compile-time DynamicTextureModule.ReuseTextures flag is set, check metadata still exists for any reused asset in case some other process has removed it from the cache. 2012-09-27 23:12:32 +01:00
SignpostMarv
8f39268761 fixing bug where last element in list is ignored 2012-09-27 23:12:24 +01:00
Justin Clark-Casey (justincc)
cd8c8d78a9 Renaming existing 'torture' tests to 'performance' tests instead, since this better matches what they really do.
nant target name changes to test-perf instead of torture, to match test-stress
still not run by default
2012-09-27 23:11:55 +01:00
Justin Clark-Casey (justincc)
980678846d Add VectorRenderModuleStressTests that contains a long running test that generates thousands of vector textures concurrently.
Intended for use if there are future issues with mono crashes whilst generate dynamic textures.
This test is triggered via a new test-stress nant target.
Not run by default.
2012-09-27 23:11:44 +01:00
Justin Clark-Casey (justincc)
9a92d8d57e Add experimental DynamicTextureModule.ReuseTextures flag, currently only configurable on compile.
Disabled (status quo) by default.
This flag makes the dynamic texture module reuse cache previously dynamically generated textures given the same input commands and extra params for 24 hours.
This occurs as long as those commands would always generate the same texture (e.g. they do not contain commands to fetch data from the web).
This makes texture changing faster as a viewer-cached texture uuid is sent and may reduce simulator load in regions with generation of lots of dynamic textures.
A downside is that this stops expiry of old temporary dynamic textures from the cache,
Another downside is that a jpeg2000 generation that partially failed is currently not regenerated until restart or after 24 hours.
2012-09-27 23:11:10 +01:00
Justin Clark-Casey (justincc)
b155c601d7 Add IDynamicTextureManager.ConvertData() to match AsyncConvertData(). Remove mismatching ConvertStream() where there is no AsyncConvertStream and neither IDynamicTextureManager implementer implements this method. 2012-09-27 23:11:04 +01:00
Justin Clark-Casey (justincc)
536f2c085a minor: Simplify return of vector render module name and some very minor removal of unncessary syntax clutter 2012-09-27 23:10:08 +01:00
SignpostMarv
2602d4f16e refactoring using List.ConvertAll<string> 2012-09-27 23:05:50 +01:00
SignpostMarv
174addc426 adding a clip method to handle Vector3 objects to enable a minor amount of refactoring 2012-09-27 23:00:01 +01:00
Justin Clark-Casey (justincc)
849053681e Change flavour to Extended 2012-09-27 00:57:34 +01:00
Justin Clark-Casey (justincc)
a8d8ea7990 Flip release type to post fixes 2012-09-01 00:01:01 +01:00
Justin Clark-Casey (justincc)
10e6493f9f Set flavour to release. 2012-08-31 21:39:51 +01:00
Melanie
8c4f911935 Replace SendBannedUserList with Avination's version. Untested in core. Not even test compiled. 2012-08-31 16:35:36 +01:00
Justin Clark-Casey (justincc)
62bc85b5c7 Fix regression introduced in a0d178b2 (Sat Aug 25 02:00:17 2012) where folders with asset type of 'Folder' and 'Unknown' were accidentally treated as system folders.
This prevented more than one additional ordinary folder from being created in the base "My Inventory" user folder.
Added regression test for this case.
Switched tests to use XInventoryService with mostly implemented TestXInventoryDataPlugin rather than InventoryService
Disabled TestLoadIarV0_1SameNameCreator() since this has not been working for a very long time (ever since XInventoryService) started being used
since it doesnt' preserve creator data in the same way as InventoryService did and so effectively lost the OSPAs.
However, nobody noticed/complained about this issue and OSPAs have been superseded by HG like creator information via the --home save oar/iar switch.
2012-08-31 16:29:57 +01:00
SignpostMarv
76eba917f9 copying documentation from http://opensimulator.org/wiki/Threat_level 2012-08-31 16:29:46 +01:00
SignpostMarv
84a046eaf5 adding some files to .gitignore that get generated when debugging in c# express with OpenSim.32BitLaunch as the startup project 2012-08-31 16:29:40 +01:00
Justin Clark-Casey (justincc)
8d1d314f49 Add VectorRenderModule.TestRepeatSameDrawDifferentExtraParams() 2012-08-31 16:29:22 +01:00
Justin Clark-Casey (justincc)
9395f12584 Add VectorRenderModuleTests.TestRepeatDrawContainingImage() 2012-08-31 16:29:16 +01:00
Justin Clark-Casey (justincc)
b97f58d597 Add VectorRenderModuleTests.TestRepeatDraw() 2012-08-31 16:29:09 +01:00
Justin Clark-Casey (justincc)
30c36a3960 Following on from f8a89a79, do not allow more than one 'type' folder (e.g. calling cards) to be created in the base "My Inventory" user folder.
This is to accomodate situations where viewers will create more than one 'type' subfolder (e.g. calling cards)
But at the same time to prevent multiple such 'system' folders (those in the base "My Inventory" user folder).
This also makes GetFolderForType() only return a folder in the base "My Inventory" folder, if such a type folder exists
2012-08-31 16:28:20 +01:00
Justin Clark-Casey (justincc)
206eccc2b6 Allow multiple calling card type inventory folders to be created.
Modern viewers want to create Friends and All folders of this type inside the root Calling Cards folder.
2012-08-31 16:28:13 +01:00
Justin Clark-Casey (justincc)
2358a623e3 minor: Fix bad log message for failure to create an inventory folder 2012-08-31 16:28:04 +01:00
Justin Clark-Casey (justincc)
87850bd6dc Log initial script startup info notice when xengine actually starts to do this for debugging purposes, rather than before it actually starts to do this. 2012-08-31 16:27:54 +01:00
Justin Clark-Casey (justincc)
d80eda202f Extend "Restarting scripts in attachments" debug log message to show actual name of user and the region they are in 2012-08-24 22:57:18 +01:00
Justin Clark-Casey (justincc)
3edfa585ec If a connecting scene presence is replacing an existing scene presence then bypass close checks. 2012-08-24 22:43:14 +01:00
Justin Clark-Casey (justincc)
fa13a6a8da Bump version number to 0.7.4-rc2 2012-08-24 22:07:58 +01:00
Justin Clark-Casey (justincc)
9d5e7c89d9 Pass the "attachToBackup" bool given to SceneGraph.AddNewSceneObject() down into the 3-parameter AddNewSceneObject() method instead of always hardcoding true.
This doesn't affect any core OpenSimulator code since all callers were passing true anyway
But it allows region modules to create objects that are never persisted.
2012-08-24 21:53:57 +01:00
SignpostMarv
590bf0bcc0 adding sqlite journal files to .gitignore 2012-08-24 21:53:46 +01:00
Justin Clark-Casey (justincc)
b199a2dea3 If a script state save fails for some reason on shutdown/region removal, get xengine to spit out some useful information and continue to save other script states 2012-08-24 21:53:22 +01:00
Justin Clark-Casey (justincc)
f1d4b8d83e Add an [HGAssetService] section to SQLiteStandalone.ini with the same connection string as [AssetService].
This is necessary because commit 8131a24 (Tue Mar 27 10:08:13 2012) started passing the config section name rather than hardcoding "AssetService"
This meant that the HG external-facing asset service tried to read ConnectionString from [HGAssetService] rather than [AssetService].
On SQLite, not finding this meant that it fell back to [DatabaseService], which is set for OpenSim.db rather than Asset.db.
Therefore, all external asset requests returned null.
Solution taken here is to create an [HGAssetService] section with the same ConnectionString as [AssetService].
This bug does not affect normal MySQL/MSSQL config since they use the [DatabaseService] connection string anyway.
Addresses http://opensimulator.org/mantis/view.php?id=6200, many thanks to DanBanner for identifying the exact problem commit which was very helpful.
This was a regression from OpenSimulator 0.7.3.1 which did not contain this bug.
2012-08-24 21:53:12 +01:00
Justin Clark-Casey (justincc)
0088661356 Lock disposal of separate gdi+ objects under different threads since this prevents malloc heap corruption seen under Ubuntu 10.04.1 and 11.04 - probably a libcairo issue
In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
the native malloc heap can become corrupted, possibly due to a double free().  This may be due to
bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX.  These problems were
seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1.  They go away if disposal is perfomed
under lock.
2012-08-24 21:53:01 +01:00
Justin Clark-Casey (justincc)
1a7e3cabc0 Fix bug in SoundModule.PlayAttachedSound() where every sound update to an avatar would base its gain calculation on the previous avatar's gain, instead of the original input gain
This is similar to commit d89faa which fixed the same kind of bug in TriggerSound()
2012-08-24 21:52:51 +01:00
Justin Clark-Casey (justincc)
132d701b3e Tighten up OpenSim.Framework.Cache locking to avoid race conditions.
This is to resolve a reported issue in http://opensimulator.org/mantis/view.php?id=6232
Here, the land management module is using OpenSim.Framework.Cache (the only code to currently do so apart from the non-default CoreAssetCache).
2012-08-24 21:52:30 +01:00
Justin Clark-Casey (justincc)
3b97241716 Add --force flag to "kick user" console command to allow bypassing of recent race condition checks.
This is to allow a second attempt to remove an avatar even if "show connections" shows them as already inactive (i.e. close has already been attempted once).
You should only attempt --force if a normal kick fails.
This is partly for diagnostics as we have seen some connections occasionally remain on lbsa plaza even if they are registered as inactive.
This is not a permanent solution and may not work anyway - the ultimate solution is to stop this problem from happening in the first place.
2012-08-24 21:52:21 +01:00
Melanie
f82d090df3 Fix llDialog responses so that they can be heard throughout the region. This now conforms to the behaviour in SL. 2012-08-24 21:52:12 +01:00
Justin Clark-Casey (justincc)
7399d3e953 When reporting a thread timeout, create a copy of the info rather than passing the original ThreadWatchdogInfo structure.
This is to avoid the possibility of misleading reporting if a watchdog update outraces an alarm.
Should address any remaining issues from http://opensimulator.org/mantis/view.php?id=6012
2012-08-24 21:51:03 +01:00
Justin Clark-Casey (justincc)
cab546ecee Add information to ThreadStackSize about possibly increasing if suffering StackOverflowExceptions during script conversion/compilation (e.g. on Windows 64-bit) 2012-08-24 21:50:49 +01:00
Justin Clark-Casey (justincc)
7dc1c7d841 minor: Make xengine debug message on script load a scripting loading message instead.
This is more useful if compilation fails due to an uncatchable exception since we know what was being compiled.
2012-08-24 21:50:40 +01:00
Robert Adams
fbff51f387 Correct an exception report in SceneObjectPart so it outputs the stack. 2012-08-24 21:50:03 +01:00
SignpostMarv
b714fb0c39 Implementing PRIM_LINK_TARGET in a non-recursive fashion 2012-08-24 21:49:44 +01:00
Justin Clark-Casey (justincc)
f37038013d Don't enable the thread watchdog until all regions are ready.
This is to avoid false positives when the machine is under heavy load whilst starting up.
2012-08-24 21:49:36 +01:00
SignpostMarv
5e98e2b7c7 adding ATTACH_*_PEC constants 2012-08-24 21:49:27 +01:00
Justin Clark-Casey (justincc)
35f8f3ff79 minor: Add Gryc Ueusp to CREDITS for commit 884edac amongst others, by request. 2012-08-24 21:48:59 +01:00
Justin Clark-Casey (justincc)
7ec8e7e025 Prevent race conditions when one thread removes an NPC SP before another thread has retreived it after checking whether the NPC exists. 2012-08-24 21:48:48 +01:00
Melanie
c6efebdd8c Release http-in URLs when llResetScript is called 2012-08-24 21:48:15 +01:00
Justin Clark-Casey (justincc)
760047abc5 Flip version to 0.7.4-rc1 2012-08-03 23:58:34 +01:00
203 changed files with 18411 additions and 2090 deletions

6
.gitignore vendored
View File

@@ -26,9 +26,14 @@
bin/Debug/*.dll
bin/*.dll.mdb
bin/*.db
bin/*.db-journal
bin/addin-db-*
bin/*.dll
bin/OpenSim.vshost.exe.config
bin/OpenSim.32BitLaunch.vshost.exe.config
bin/OpenSim.32BitLaunch.log
UpgradeLog.XML
_UpgradeReport_Files/
bin/ScriptEngines/*-*-*-*-*
bin/ScriptEngines/*.dll
bin/ScriptEngines/*/*.dll
@@ -61,6 +66,7 @@ Examples/*.dll
OpenSim.build
OpenSim.sln
OpenSim.suo
OpenSim.userprefs
Prebuild/Prebuild.build
Prebuild/Prebuild.sln
TestResult.xml

View File

@@ -135,14 +135,25 @@
<delete dir="%temp%"/>
</target>
<target name="torture" depends="build, find-nunit">
<target name="test-stress" depends="build, find-nunit">
<setenv name="MONO_THREADS_PER_CPU" value="100" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.torture">
<arg value="./bin/OpenSim.Tests.Torture.dll" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.stress">
<arg value="./bin/OpenSim.Tests.Stress.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests.torture)==0}" />
<fail message="Failures reported in stress tests." unless="${int::parse(testresult.opensim.tests.stress)==0}" />
<delete dir="%temp%"/>
</target>
<target name="test-perf" depends="build, find-nunit">
<setenv name="MONO_THREADS_PER_CPU" value="100" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.performance">
<arg value="./bin/OpenSim.Tests.Performance.dll" />
</exec>
<fail message="Failures reported in performance tests." unless="${int::parse(testresult.opensim.tests.performance)==0}" />
<delete dir="%temp%"/>
</target>

View File

@@ -91,6 +91,7 @@ what it is today.
* Fly-Man
* Flyte Xevious
* Garmin Kawaguichi
* Gryc Ueusp
* Imaze Rhiano
* Intimidated
* Jeremy Bongio (IBM)

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
@@ -982,8 +982,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 +1180,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"]);

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

@@ -40,6 +40,11 @@ namespace OpenSim.Data
public UUID folderID;
public UUID agentID;
public UUID parentFolderID;
public XInventoryFolder Clone()
{
return (XInventoryFolder)MemberwiseClone();
}
}
public class XInventoryItem
@@ -64,6 +69,11 @@ namespace OpenSim.Data
public UUID avatarID;
public UUID parentFolderID;
public int inventoryGroupPermissions;
public XInventoryItem Clone()
{
return (XInventoryItem)MemberwiseClone();
}
}
public interface IXInventoryData
@@ -106,7 +116,22 @@ namespace OpenSim.Data
/// <returns>true if the delete was successful, false if it was not</returns>
bool DeleteItems(string[] fields, string[] vals);
bool MoveItem(string id, string newParent);
/// <summary>
/// Move an item to another folder.
/// </summary>
/// <returns>/returns>
/// <param name='id'>UUID of the item</param>
/// <param name='newParent'>UUID of the new parent folder.</param>
bool MoveItem(string id, string newParentFolderID);
/// <summary>
/// Move a folder to another folder.
/// </summary>
/// <returns>/returns>
/// <param name='id'>UUID of the item</param>
/// <param name='newParent'>UUID of the new parent folder.</param>
bool MoveFolder(string id, string newParentFolderID);
XInventoryItem[] GetActiveGestures(UUID principalID);
int GetAssetPermissions(UUID principalID, UUID assetID);
}

View File

@@ -43,12 +43,12 @@ namespace OpenSim.Data.MSSQL
// private static readonly ILog m_log = LogManager.GetLogger(
// MethodBase.GetCurrentMethod().DeclaringType);
private MSSQLGenericTableHandler<XInventoryFolder> m_Folders;
private MSSQLFolderHandler m_Folders;
private MSSQLItemHandler m_Items;
public MSSQLXInventoryData(string conn, string realm)
{
m_Folders = new MSSQLGenericTableHandler<XInventoryFolder>(
m_Folders = new MSSQLFolderHandler(
conn, "inventoryfolders", "InventoryStore");
m_Items = new MSSQLItemHandler(
conn, "inventoryitems", String.Empty);
@@ -85,6 +85,7 @@ namespace OpenSim.Data.MSSQL
{
return m_Folders.Delete(field, val);
}
public bool DeleteFolders(string[] fields, string[] vals)
{
return m_Folders.Delete(fields, vals);
@@ -94,15 +95,22 @@ namespace OpenSim.Data.MSSQL
{
return m_Items.Delete(field, val);
}
public bool DeleteItems(string[] fields, string[] vals)
{
return m_Items.Delete(fields, vals);
}
public bool MoveItem(string id, string newParent)
{
return m_Items.MoveItem(id, newParent);
}
public bool MoveFolder(string id, string newParent)
{
return m_Folders.MoveFolder(id, newParent);
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
return m_Items.GetActiveGestures(principalID);
@@ -124,79 +132,115 @@ namespace OpenSim.Data.MSSQL
public bool MoveItem(string id, string newParent)
{
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = String.Format("update {0} set parentFolderID = @ParentFolderID where inventoryID = @InventoryID", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@ParentFolderID", newParent));
cmd.Parameters.Add(m_database.CreateParameter("@InventoryID", id));
cmd.Connection = conn;
conn.Open();
return cmd.ExecuteNonQuery() == 0 ? false : true;
cmd.CommandText = String.Format("update {0} set parentFolderID = @ParentFolderID where inventoryID = @InventoryID", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@ParentFolderID", newParent));
cmd.Parameters.Add(m_database.CreateParameter("@InventoryID", id));
cmd.Connection = conn;
conn.Open();
return cmd.ExecuteNonQuery() == 0 ? false : true;
}
}
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = @uuid and assetType = @type and flags = 1", m_Realm);
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = @uuid and assetType = @type and flags = 1", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@uuid", principalID.ToString()));
cmd.Parameters.Add(m_database.CreateParameter("@type", (int)AssetType.Gesture));
cmd.Connection = conn;
conn.Open();
return DoQuery(cmd);
cmd.Parameters.Add(m_database.CreateParameter("@uuid", principalID.ToString()));
cmd.Parameters.Add(m_database.CreateParameter("@type", (int)AssetType.Gesture));
cmd.Connection = conn;
conn.Open();
return DoQuery(cmd);
}
}
}
public int GetAssetPermissions(UUID principalID, UUID assetID)
{
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = String.Format("select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions from inventoryitems where avatarID = @PrincipalID and assetID = @AssetID group by assetID", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@PrincipalID", principalID.ToString()));
cmd.Parameters.Add(m_database.CreateParameter("@AssetID", assetID.ToString()));
cmd.Connection = conn;
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
using (SqlCommand cmd = new SqlCommand())
{
int perms = 0;
if (reader.Read())
cmd.CommandText = String.Format("select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions from inventoryitems where avatarID = @PrincipalID and assetID = @AssetID group by assetID", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@PrincipalID", principalID.ToString()));
cmd.Parameters.Add(m_database.CreateParameter("@AssetID", assetID.ToString()));
cmd.Connection = conn;
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
perms = Convert.ToInt32(reader["inventoryCurrentPermissions"]);
int perms = 0;
if (reader.Read())
{
perms = Convert.ToInt32(reader["inventoryCurrentPermissions"]);
}
return perms;
}
return perms;
}
}
}
public override bool Store(XInventoryItem item)
{
if (!base.Store(item))
return false;
string sql = "update inventoryfolders set version=version+1 where folderID = @folderID";
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("@folderID", item.parentFolderID.ToString());
try
{
cmd.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
cmd.Parameters.AddWithValue("@folderID", item.parentFolderID.ToString());
try
{
cmd.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
}
return true;
}
return true;
}
}
}
public class MSSQLFolderHandler : MSSQLGenericTableHandler<XInventoryFolder>
{
public MSSQLFolderHandler(string c, string t, string m) :
base(c, t, m)
{
}
public bool MoveFolder(string id, string newParentFolderID)
{
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = String.Format("update {0} set parentFolderID = @ParentFolderID where folderID = @folderID", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("@ParentFolderID", newParentFolderID));
cmd.Parameters.Add(m_database.CreateParameter("@folderID", id));
cmd.Connection = conn;
conn.Open();
return cmd.ExecuteNonQuery() == 0 ? false : true;
}
}
}
}
}

View File

@@ -219,6 +219,8 @@ namespace OpenSim.Data.MySQL
public virtual bool Store(T row)
{
// m_log.DebugFormat("[MYSQL GENERIC TABLE HANDLER]: Store(T row) invoked");
using (MySqlCommand cmd = new MySqlCommand())
{
string query = "";
@@ -273,6 +275,10 @@ namespace OpenSim.Data.MySQL
public virtual bool Delete(string[] fields, string[] keys)
{
// m_log.DebugFormat(
// "[MYSQL GENERIC TABLE HANDLER]: Delete(string[] fields, string[] keys) invoked with {0}:{1}",
// string.Join(",", fields), string.Join(",", keys));
if (fields.Length != keys.Length)
return false;

View File

@@ -719,95 +719,99 @@ namespace OpenSim.Data.MySQL
RegionLightShareData nWP = new RegionLightShareData();
nWP.OnSave += StoreRegionWindlightSettings;
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
string command = "select * from `regionwindlight` where region_id = ?regionID";
using (MySqlCommand cmd = new MySqlCommand(command))
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.Connection = dbcon;
cmd.Parameters.AddWithValue("?regionID", regionUUID.ToString());
IDataReader result = ExecuteReader(cmd);
if (!result.Read())
dbcon.Open();
string command = "select * from `regionwindlight` where region_id = ?regionID";
using (MySqlCommand cmd = new MySqlCommand(command))
{
//No result, so store our default windlight profile and return it
nWP.regionID = regionUUID;
StoreRegionWindlightSettings(nWP);
return nWP;
}
else
{
nWP.regionID = DBGuid.FromDB(result["region_id"]);
nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]);
nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]);
nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]);
nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]);
nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]);
nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]);
nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]);
nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]);
nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]);
nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]);
nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]);
nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]);
nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]);
nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]);
nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]);
nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]);
nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]);
UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture);
nWP.horizon.X = Convert.ToSingle(result["horizon_r"]);
nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]);
nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]);
nWP.horizon.W = Convert.ToSingle(result["horizon_i"]);
nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]);
nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]);
nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]);
nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]);
nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]);
nWP.hazeDensity = Convert.ToSingle(result["haze_density"]);
nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]);
nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]);
nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]);
nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]);
nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]);
nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]);
nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]);
nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]);
nWP.ambient.X = Convert.ToSingle(result["ambient_r"]);
nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]);
nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]);
nWP.ambient.W = Convert.ToSingle(result["ambient_i"]);
nWP.eastAngle = Convert.ToSingle(result["east_angle"]);
nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]);
nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]);
nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]);
nWP.starBrightness = Convert.ToSingle(result["star_brightness"]);
nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]);
nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]);
nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]);
nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]);
nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]);
nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]);
nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]);
nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]);
nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]);
nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]);
nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]);
nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]);
nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]);
nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]);
nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]);
nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]);
nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]);
nWP.valid = true;
cmd.Connection = dbcon;
cmd.Parameters.AddWithValue("?regionID", regionUUID.ToString());
IDataReader result = ExecuteReader(cmd);
if (!result.Read())
{
//No result, so store our default windlight profile and return it
nWP.regionID = regionUUID;
StoreRegionWindlightSettings(nWP);
return nWP;
}
else
{
nWP.regionID = DBGuid.FromDB(result["region_id"]);
nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]);
nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]);
nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]);
nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]);
nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]);
nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]);
nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]);
nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]);
nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]);
nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]);
nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]);
nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]);
nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]);
nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]);
nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]);
nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]);
nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]);
UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture);
nWP.horizon.X = Convert.ToSingle(result["horizon_r"]);
nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]);
nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]);
nWP.horizon.W = Convert.ToSingle(result["horizon_i"]);
nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]);
nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]);
nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]);
nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]);
nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]);
nWP.hazeDensity = Convert.ToSingle(result["haze_density"]);
nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]);
nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]);
nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]);
nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]);
nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]);
nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]);
nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]);
nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]);
nWP.ambient.X = Convert.ToSingle(result["ambient_r"]);
nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]);
nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]);
nWP.ambient.W = Convert.ToSingle(result["ambient_i"]);
nWP.eastAngle = Convert.ToSingle(result["east_angle"]);
nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]);
nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]);
nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]);
nWP.starBrightness = Convert.ToSingle(result["star_brightness"]);
nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]);
nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]);
nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]);
nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]);
nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]);
nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]);
nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]);
nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]);
nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]);
nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]);
nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]);
nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]);
nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]);
nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]);
nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]);
nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]);
nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]);
nWP.valid = true;
}
}
}
}
return nWP;
}
@@ -853,118 +857,124 @@ namespace OpenSim.Data.MySQL
public void StoreRegionWindlightSettings(RegionLightShareData wl)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.CommandText = "REPLACE INTO `regionwindlight` (`region_id`, `water_color_r`, `water_color_g`, ";
cmd.CommandText += "`water_color_b`, `water_fog_density_exponent`, `underwater_fog_modifier`, ";
cmd.CommandText += "`reflection_wavelet_scale_1`, `reflection_wavelet_scale_2`, `reflection_wavelet_scale_3`, ";
cmd.CommandText += "`fresnel_scale`, `fresnel_offset`, `refract_scale_above`, `refract_scale_below`, ";
cmd.CommandText += "`blur_multiplier`, `big_wave_direction_x`, `big_wave_direction_y`, `little_wave_direction_x`, ";
cmd.CommandText += "`little_wave_direction_y`, `normal_map_texture`, `horizon_r`, `horizon_g`, `horizon_b`, ";
cmd.CommandText += "`horizon_i`, `haze_horizon`, `blue_density_r`, `blue_density_g`, `blue_density_b`, ";
cmd.CommandText += "`blue_density_i`, `haze_density`, `density_multiplier`, `distance_multiplier`, `max_altitude`, ";
cmd.CommandText += "`sun_moon_color_r`, `sun_moon_color_g`, `sun_moon_color_b`, `sun_moon_color_i`, `sun_moon_position`, ";
cmd.CommandText += "`ambient_r`, `ambient_g`, `ambient_b`, `ambient_i`, `east_angle`, `sun_glow_focus`, `sun_glow_size`, ";
cmd.CommandText += "`scene_gamma`, `star_brightness`, `cloud_color_r`, `cloud_color_g`, `cloud_color_b`, `cloud_color_i`, ";
cmd.CommandText += "`cloud_x`, `cloud_y`, `cloud_density`, `cloud_coverage`, `cloud_scale`, `cloud_detail_x`, ";
cmd.CommandText += "`cloud_detail_y`, `cloud_detail_density`, `cloud_scroll_x`, `cloud_scroll_x_lock`, `cloud_scroll_y`, ";
cmd.CommandText += "`cloud_scroll_y_lock`, `draw_classic_clouds`) VALUES (?region_id, ?water_color_r, ";
cmd.CommandText += "?water_color_g, ?water_color_b, ?water_fog_density_exponent, ?underwater_fog_modifier, ?reflection_wavelet_scale_1, ";
cmd.CommandText += "?reflection_wavelet_scale_2, ?reflection_wavelet_scale_3, ?fresnel_scale, ?fresnel_offset, ?refract_scale_above, ";
cmd.CommandText += "?refract_scale_below, ?blur_multiplier, ?big_wave_direction_x, ?big_wave_direction_y, ?little_wave_direction_x, ";
cmd.CommandText += "?little_wave_direction_y, ?normal_map_texture, ?horizon_r, ?horizon_g, ?horizon_b, ?horizon_i, ?haze_horizon, ";
cmd.CommandText += "?blue_density_r, ?blue_density_g, ?blue_density_b, ?blue_density_i, ?haze_density, ?density_multiplier, ";
cmd.CommandText += "?distance_multiplier, ?max_altitude, ?sun_moon_color_r, ?sun_moon_color_g, ?sun_moon_color_b, ";
cmd.CommandText += "?sun_moon_color_i, ?sun_moon_position, ?ambient_r, ?ambient_g, ?ambient_b, ?ambient_i, ?east_angle, ";
cmd.CommandText += "?sun_glow_focus, ?sun_glow_size, ?scene_gamma, ?star_brightness, ?cloud_color_r, ?cloud_color_g, ";
cmd.CommandText += "?cloud_color_b, ?cloud_color_i, ?cloud_x, ?cloud_y, ?cloud_density, ?cloud_coverage, ?cloud_scale, ";
cmd.CommandText += "?cloud_detail_x, ?cloud_detail_y, ?cloud_detail_density, ?cloud_scroll_x, ?cloud_scroll_x_lock, ";
cmd.CommandText += "?cloud_scroll_y, ?cloud_scroll_y_lock, ?draw_classic_clouds)";
cmd.Parameters.AddWithValue("region_id", wl.regionID);
cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X);
cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y);
cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z);
cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent);
cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z);
cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale);
cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset);
cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove);
cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow);
cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier);
cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X);
cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y);
cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X);
cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y);
cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture);
cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X);
cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y);
cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z);
cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W);
cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon);
cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X);
cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y);
cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z);
cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W);
cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity);
cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier);
cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier);
cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude);
cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X);
cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y);
cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z);
cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W);
cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition);
cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X);
cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y);
cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z);
cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W);
cmd.Parameters.AddWithValue("east_angle", wl.eastAngle);
cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus);
cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize);
cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma);
cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness);
cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X);
cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y);
cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z);
cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W);
cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X);
cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y);
cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z);
cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage);
cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale);
cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X);
cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y);
cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z);
cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX);
cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock);
cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY);
cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock);
cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds);
ExecuteNonQuery(cmd);
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
{
cmd.CommandText = "REPLACE INTO `regionwindlight` (`region_id`, `water_color_r`, `water_color_g`, ";
cmd.CommandText += "`water_color_b`, `water_fog_density_exponent`, `underwater_fog_modifier`, ";
cmd.CommandText += "`reflection_wavelet_scale_1`, `reflection_wavelet_scale_2`, `reflection_wavelet_scale_3`, ";
cmd.CommandText += "`fresnel_scale`, `fresnel_offset`, `refract_scale_above`, `refract_scale_below`, ";
cmd.CommandText += "`blur_multiplier`, `big_wave_direction_x`, `big_wave_direction_y`, `little_wave_direction_x`, ";
cmd.CommandText += "`little_wave_direction_y`, `normal_map_texture`, `horizon_r`, `horizon_g`, `horizon_b`, ";
cmd.CommandText += "`horizon_i`, `haze_horizon`, `blue_density_r`, `blue_density_g`, `blue_density_b`, ";
cmd.CommandText += "`blue_density_i`, `haze_density`, `density_multiplier`, `distance_multiplier`, `max_altitude`, ";
cmd.CommandText += "`sun_moon_color_r`, `sun_moon_color_g`, `sun_moon_color_b`, `sun_moon_color_i`, `sun_moon_position`, ";
cmd.CommandText += "`ambient_r`, `ambient_g`, `ambient_b`, `ambient_i`, `east_angle`, `sun_glow_focus`, `sun_glow_size`, ";
cmd.CommandText += "`scene_gamma`, `star_brightness`, `cloud_color_r`, `cloud_color_g`, `cloud_color_b`, `cloud_color_i`, ";
cmd.CommandText += "`cloud_x`, `cloud_y`, `cloud_density`, `cloud_coverage`, `cloud_scale`, `cloud_detail_x`, ";
cmd.CommandText += "`cloud_detail_y`, `cloud_detail_density`, `cloud_scroll_x`, `cloud_scroll_x_lock`, `cloud_scroll_y`, ";
cmd.CommandText += "`cloud_scroll_y_lock`, `draw_classic_clouds`) VALUES (?region_id, ?water_color_r, ";
cmd.CommandText += "?water_color_g, ?water_color_b, ?water_fog_density_exponent, ?underwater_fog_modifier, ?reflection_wavelet_scale_1, ";
cmd.CommandText += "?reflection_wavelet_scale_2, ?reflection_wavelet_scale_3, ?fresnel_scale, ?fresnel_offset, ?refract_scale_above, ";
cmd.CommandText += "?refract_scale_below, ?blur_multiplier, ?big_wave_direction_x, ?big_wave_direction_y, ?little_wave_direction_x, ";
cmd.CommandText += "?little_wave_direction_y, ?normal_map_texture, ?horizon_r, ?horizon_g, ?horizon_b, ?horizon_i, ?haze_horizon, ";
cmd.CommandText += "?blue_density_r, ?blue_density_g, ?blue_density_b, ?blue_density_i, ?haze_density, ?density_multiplier, ";
cmd.CommandText += "?distance_multiplier, ?max_altitude, ?sun_moon_color_r, ?sun_moon_color_g, ?sun_moon_color_b, ";
cmd.CommandText += "?sun_moon_color_i, ?sun_moon_position, ?ambient_r, ?ambient_g, ?ambient_b, ?ambient_i, ?east_angle, ";
cmd.CommandText += "?sun_glow_focus, ?sun_glow_size, ?scene_gamma, ?star_brightness, ?cloud_color_r, ?cloud_color_g, ";
cmd.CommandText += "?cloud_color_b, ?cloud_color_i, ?cloud_x, ?cloud_y, ?cloud_density, ?cloud_coverage, ?cloud_scale, ";
cmd.CommandText += "?cloud_detail_x, ?cloud_detail_y, ?cloud_detail_density, ?cloud_scroll_x, ?cloud_scroll_x_lock, ";
cmd.CommandText += "?cloud_scroll_y, ?cloud_scroll_y_lock, ?draw_classic_clouds)";
cmd.Parameters.AddWithValue("region_id", wl.regionID);
cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X);
cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y);
cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z);
cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent);
cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y);
cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z);
cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale);
cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset);
cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove);
cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow);
cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier);
cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X);
cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y);
cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X);
cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y);
cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture);
cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X);
cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y);
cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z);
cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W);
cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon);
cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X);
cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y);
cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z);
cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W);
cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity);
cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier);
cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier);
cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude);
cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X);
cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y);
cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z);
cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W);
cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition);
cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X);
cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y);
cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z);
cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W);
cmd.Parameters.AddWithValue("east_angle", wl.eastAngle);
cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus);
cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize);
cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma);
cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness);
cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X);
cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y);
cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z);
cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W);
cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X);
cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y);
cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z);
cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage);
cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale);
cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X);
cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y);
cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z);
cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX);
cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock);
cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY);
cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock);
cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds);
ExecuteNonQuery(cmd);
}
}
}
}
public void RemoveRegionWindlightSettings(UUID regionID)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.CommandText = "delete from `regionwindlight` where `region_id`=?regionID";
cmd.Parameters.AddWithValue("?regionID", regionID.ToString());
ExecuteNonQuery(cmd);
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
{
cmd.CommandText = "delete from `regionwindlight` where `region_id`=?regionID";
cmd.Parameters.AddWithValue("?regionID", regionID.ToString());
ExecuteNonQuery(cmd);
}
}
}
}
@@ -972,26 +982,29 @@ namespace OpenSim.Data.MySQL
#region RegionEnvironmentSettings
public string LoadRegionEnvironmentSettings(UUID regionUUID)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
string command = "select * from `regionenvironment` where region_id = ?region_id";
using (MySqlCommand cmd = new MySqlCommand(command))
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.Connection = dbcon;
cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
IDataReader result = ExecuteReader(cmd);
if (!result.Read())
dbcon.Open();
string command = "select * from `regionenvironment` where region_id = ?region_id";
using (MySqlCommand cmd = new MySqlCommand(command))
{
return String.Empty;
}
else
{
return Convert.ToString(result["llsd_settings"]);
cmd.Connection = dbcon;
cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
IDataReader result = ExecuteReader(cmd);
if (!result.Read())
{
return String.Empty;
}
else
{
return Convert.ToString(result["llsd_settings"]);
}
}
}
}
@@ -999,33 +1012,39 @@ namespace OpenSim.Data.MySQL
public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.CommandText = "REPLACE INTO `regionenvironment` (`region_id`, `llsd_settings`) VALUES (?region_id, ?llsd_settings)";
cmd.Parameters.AddWithValue("region_id", regionUUID);
cmd.Parameters.AddWithValue("llsd_settings", settings);
ExecuteNonQuery(cmd);
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
{
cmd.CommandText = "REPLACE INTO `regionenvironment` (`region_id`, `llsd_settings`) VALUES (?region_id, ?llsd_settings)";
cmd.Parameters.AddWithValue("region_id", regionUUID);
cmd.Parameters.AddWithValue("llsd_settings", settings);
ExecuteNonQuery(cmd);
}
}
}
}
public void RemoveRegionEnvironmentSettings(UUID regionUUID)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
lock (m_dbLock)
{
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
cmd.CommandText = "delete from `regionenvironment` where region_id = ?region_id";
cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
ExecuteNonQuery(cmd);
dbcon.Open();
using (MySqlCommand cmd = dbcon.CreateCommand())
{
cmd.CommandText = "delete from `regionenvironment` where region_id = ?region_id";
cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
ExecuteNonQuery(cmd);
}
}
}
}

View File

@@ -26,9 +26,10 @@
*/
using System;
using System.Data;
using System.Reflection;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using log4net;
using MySql.Data.MySqlClient;
using OpenMetaverse;
@@ -41,12 +42,12 @@ namespace OpenSim.Data.MySQL
/// </summary>
public class MySQLXInventoryData : IXInventoryData
{
private MySQLGenericTableHandler<XInventoryFolder> m_Folders;
private MySqlFolderHandler m_Folders;
private MySqlItemHandler m_Items;
public MySQLXInventoryData(string conn, string realm)
{
m_Folders = new MySQLGenericTableHandler<XInventoryFolder>(
m_Folders = new MySqlFolderHandler(
conn, "inventoryfolders", "InventoryStore");
m_Items = new MySqlItemHandler(
conn, "inventoryitems", String.Empty);
@@ -105,6 +106,11 @@ namespace OpenSim.Data.MySQL
return m_Items.MoveItem(id, newParent);
}
public bool MoveFolder(string id, string newParent)
{
return m_Folders.MoveFolder(id, newParent);
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
return m_Items.GetActiveGestures(principalID);
@@ -118,22 +124,69 @@ namespace OpenSim.Data.MySQL
public class MySqlItemHandler : MySQLGenericTableHandler<XInventoryItem>
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public MySqlItemHandler(string c, string t, string m) :
base(c, t, m)
{
}
public override bool Delete(string field, string val)
{
XInventoryItem[] retrievedItems = Get(new string[] { field }, new string[] { val });
if (retrievedItems.Length == 0)
return false;
if (!base.Delete(field, val))
return false;
// Don't increment folder version here since Delete(string, string) calls Delete(string[], string[])
// IncrementFolderVersion(retrievedItems[0].parentFolderID);
return true;
}
public override bool Delete(string[] fields, string[] vals)
{
XInventoryItem[] retrievedItems = Get(fields, vals);
if (retrievedItems.Length == 0)
return false;
if (!base.Delete(fields, vals))
return false;
HashSet<UUID> deletedItemFolderUUIDs = new HashSet<UUID>();
Array.ForEach<XInventoryItem>(retrievedItems, i => deletedItemFolderUUIDs.Add(i.parentFolderID));
foreach (UUID deletedItemFolderUUID in deletedItemFolderUUIDs)
IncrementFolderVersion(deletedItemFolderUUID);
return true;
}
public bool MoveItem(string id, string newParent)
{
XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id });
if (retrievedItems.Length == 0)
return false;
UUID oldParent = retrievedItems[0].parentFolderID;
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.CommandText = String.Format("update {0} set parentFolderID = ?ParentFolderID where inventoryID = ?InventoryID", m_Realm);
cmd.Parameters.AddWithValue("?ParentFolderID", newParent);
cmd.Parameters.AddWithValue("?InventoryID", id);
return ExecuteNonQuery(cmd) == 0 ? false : true;
if (ExecuteNonQuery(cmd) == 0)
return false;
}
IncrementFolderVersion(oldParent);
IncrementFolderVersion(newParent);
return true;
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
@@ -184,6 +237,21 @@ namespace OpenSim.Data.MySQL
if (!base.Store(item))
return false;
IncrementFolderVersion(item.parentFolderID);
return true;
}
private bool IncrementFolderVersion(UUID folderID)
{
return IncrementFolderVersion(folderID.ToString());
}
private bool IncrementFolderVersion(string folderID)
{
// m_log.DebugFormat("[MYSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID);
// Util.PrintCallStack();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
@@ -193,7 +261,7 @@ namespace OpenSim.Data.MySQL
cmd.Connection = dbcon;
cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID");
cmd.Parameters.AddWithValue("?folderID", item.parentFolderID.ToString());
cmd.Parameters.AddWithValue("?folderID", folderID);
try
{
@@ -205,9 +273,96 @@ namespace OpenSim.Data.MySQL
}
cmd.Dispose();
}
dbcon.Close();
}
return true;
}
}
}
public class MySqlFolderHandler : MySQLGenericTableHandler<XInventoryFolder>
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public MySqlFolderHandler(string c, string t, string m) :
base(c, t, m)
{
}
public bool MoveFolder(string id, string newParentFolderID)
{
XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id });
if (folders.Length == 0)
return false;
UUID oldParentFolderUUID = folders[0].parentFolderID;
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.CommandText
= String.Format(
"update {0} set parentFolderID = ?ParentFolderID where folderID = ?folderID", m_Realm);
cmd.Parameters.AddWithValue("?ParentFolderID", newParentFolderID);
cmd.Parameters.AddWithValue("?folderID", id);
if (ExecuteNonQuery(cmd) == 0)
return false;
}
IncrementFolderVersion(oldParentFolderUUID);
IncrementFolderVersion(newParentFolderID);
return true;
}
public override bool Store(XInventoryFolder folder)
{
if (!base.Store(folder))
return false;
IncrementFolderVersion(folder.parentFolderID);
return true;
}
private bool IncrementFolderVersion(UUID folderID)
{
return IncrementFolderVersion(folderID.ToString());
}
private bool IncrementFolderVersion(string folderID)
{
// m_log.DebugFormat("[MYSQL FOLDER HANDLER]: Incrementing version on folder {0}", folderID);
// Util.PrintCallStack();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = dbcon;
cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID");
cmd.Parameters.AddWithValue("?folderID", folderID);
try
{
cmd.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
cmd.Dispose();
}
dbcon.Close();
}
return true;
}
}
}

View File

@@ -47,7 +47,7 @@ namespace OpenSim.Data.SQLite
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private SQLiteGenericTableHandler<XInventoryFolder> m_Folders;
private SqliteFolderHandler m_Folders;
private SqliteItemHandler m_Items;
public SQLiteXInventoryData(string conn, string realm)
@@ -55,7 +55,7 @@ namespace OpenSim.Data.SQLite
if (Util.IsWindows())
Util.LoadArchSpecificWindowsDll("sqlite3.dll");
m_Folders = new SQLiteGenericTableHandler<XInventoryFolder>(
m_Folders = new SqliteFolderHandler(
conn, "inventoryfolders", "XInventoryStore");
m_Items = new SqliteItemHandler(
conn, "inventoryitems", String.Empty);
@@ -114,6 +114,11 @@ namespace OpenSim.Data.SQLite
return m_Items.MoveItem(id, newParent);
}
public bool MoveFolder(string id, string newParent)
{
return m_Folders.MoveFolder(id, newParent);
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
return m_Items.GetActiveGestures(principalID);
@@ -177,4 +182,23 @@ namespace OpenSim.Data.SQLite
return perms;
}
}
}
public class SqliteFolderHandler : SQLiteGenericTableHandler<XInventoryFolder>
{
public SqliteFolderHandler(string c, string t, string m) :
base(c, t, m)
{
}
public bool MoveFolder(string id, string newParentFolderID)
{
SqliteCommand cmd = new SqliteCommand();
cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where folderID = :FolderID", m_Realm);
cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParentFolderID));
cmd.Parameters.Add(new SqliteParameter(":FolderID", id));
return ExecuteNonQuery(cmd, m_Connection) == 0 ? false : true;
}
}
}

View File

@@ -199,7 +199,14 @@ namespace OpenSim.Framework
//
public class Cache
{
/// <summary>
/// Must only be accessed under lock.
/// </summary>
private List<CacheItemBase> m_Index = new List<CacheItemBase>();
/// <summary>
/// Must only be accessed under m_Index lock.
/// </summary>
private Dictionary<string, CacheItemBase> m_Lookup =
new Dictionary<string, CacheItemBase>();
@@ -320,19 +327,19 @@ namespace OpenSim.Framework
{
if (m_Lookup.ContainsKey(index))
item = m_Lookup[index];
}
if (item == null)
{
if (item == null)
{
Expire(true);
return null;
}
item.hits++;
item.lastUsed = DateTime.Now;
Expire(true);
return null;
}
item.hits++;
item.lastUsed = DateTime.Now;
Expire(true);
return item;
}
@@ -385,7 +392,10 @@ namespace OpenSim.Framework
//
public Object Find(Predicate<CacheItemBase> d)
{
CacheItemBase item = m_Index.Find(d);
CacheItemBase item;
lock (m_Index)
item = m_Index.Find(d);
if (item == null)
return null;
@@ -419,12 +429,12 @@ namespace OpenSim.Framework
public virtual void Store(string index, Object data, Type container,
Object[] parameters)
{
Expire(false);
CacheItemBase item;
lock (m_Index)
{
Expire(false);
if (m_Index.Contains(new CacheItemBase(index)))
{
if ((m_Flags & CacheFlags.AllowUpdate) != 0)
@@ -450,9 +460,17 @@ namespace OpenSim.Framework
m_Index.Add(item);
m_Lookup[index] = item;
}
item.Store(data);
}
/// <summary>
/// Expire items as appropriate.
/// </summary>
/// <remarks>
/// Callers must lock m_Index.
/// </remarks>
/// <param name='getting'></param>
protected virtual void Expire(bool getting)
{
if (getting && (m_Strategy == CacheStrategy.Aggressive))
@@ -475,12 +493,10 @@ namespace OpenSim.Framework
switch (m_Strategy)
{
case CacheStrategy.Aggressive:
if (Count < Size)
return;
case CacheStrategy.Aggressive:
if (Count < Size)
return;
lock (m_Index)
{
m_Index.Sort(new SortLRU());
m_Index.Reverse();
@@ -490,7 +506,7 @@ namespace OpenSim.Framework
ExpireDelegate doExpire = OnExpire;
if (doExpire != null)
if (doExpire != null)
{
List<CacheItemBase> candidates =
m_Index.GetRange(target, Count - target);
@@ -513,27 +529,34 @@ namespace OpenSim.Framework
foreach (CacheItemBase item in m_Index)
m_Lookup[item.uuid] = item;
}
}
break;
default:
break;
break;
default:
break;
}
}
public void Invalidate(string uuid)
{
if (!m_Lookup.ContainsKey(uuid))
return;
lock (m_Index)
{
if (!m_Lookup.ContainsKey(uuid))
return;
CacheItemBase item = m_Lookup[uuid];
m_Lookup.Remove(uuid);
m_Index.Remove(item);
CacheItemBase item = m_Lookup[uuid];
m_Lookup.Remove(uuid);
m_Index.Remove(item);
}
}
public void Clear()
{
m_Index.Clear();
m_Lookup.Clear();
lock (m_Index)
{
m_Index.Clear();
m_Lookup.Clear();
}
}
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.Reflection;
using log4net;
using OpenMetaverse;
public class ConsoleUtil
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public const string MinRawConsoleVectorValue = "-~";
public const string MaxRawConsoleVectorValue = "~";
public const string VectorSeparator = ",";
public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray();
/// <summary>
/// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
/// </summary>
/// <param name='rawConsoleVector'>/param>
/// <param name='vector'></param>
/// <returns></returns>
public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector)
{
return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector);
}
/// <summary>
/// Convert a maximum vector input from the console to an OpenMetaverse.Vector3
/// </summary>
/// <param name='rawConsoleVector'>/param>
/// <param name='vector'></param>
/// <returns></returns>
public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector)
{
return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector);
}
/// <summary>
/// Convert a vector input from the console to an OpenMetaverse.Vector3
/// </summary>
/// <param name='rawConsoleVector'>
/// A string in the form <x>,<y>,<z> where there is no space between values.
/// Any component can be missing (e.g. ,,40). blankComponentFunc is invoked to replace the blank with a suitable value
/// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40,30 or 40)
/// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue
/// Other than that, component values must be numeric.
/// </param>
/// <param name='blankComponentFunc'></param>
/// <param name='vector'></param>
/// <returns></returns>
public static bool TryParseConsoleVector(
string rawConsoleVector, Func<string, string> blankComponentFunc, out Vector3 vector)
{
List<string> components = rawConsoleVector.Split(VectorSeparatorChars).ToList();
if (components.Count < 1 || components.Count > 3)
{
vector = Vector3.Zero;
return false;
}
for (int i = components.Count; i < 3; i++)
components.Add("");
List<string> semiDigestedComponents
= components.ConvertAll<string>(
c =>
{
if (c == "")
return blankComponentFunc.Invoke(c);
else if (c == MaxRawConsoleVectorValue)
return float.MaxValue.ToString();
else if (c == MinRawConsoleVectorValue)
return float.MinValue.ToString();
else
return c;
});
string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray());
// m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector);
return Vector3.TryParse(semiDigestedConsoleVector, out vector);
}
}

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

@@ -805,8 +805,23 @@ namespace OpenSim.Framework
event Action<IClientAPI> OnRegionHandShakeReply;
event GenericCall1 OnRequestWearables;
event Action<IClientAPI, bool> OnCompleteMovementToRegion;
/// <summary>
/// Called when an AgentUpdate message is received and before OnAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
/// </remarks>
event UpdateAgent OnPreAgentUpdate;
/// <summary>
/// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
/// </remarks>
event UpdateAgent OnAgentUpdate;
event AgentRequestSit OnAgentRequestSit;
event AgentSit OnAgentSit;
event AvatarPickerRequest OnAvatarPickerRequest;
@@ -1033,7 +1048,21 @@ namespace OpenSim.Framework
void InPacket(object NewPack);
void ProcessInPacket(Packet NewPack);
/// <summary>
/// Close this client
/// </summary>
void Close();
/// <summary>
/// Close this client
/// </summary>
/// <param name='force'>
/// If true, attempts the close without checking active status. You do not want to try this except as a last
/// ditch attempt where Active == false but the ScenePresence still exists.
/// </param>
void Close(bool force);
void Kick(string message);
/// <summary>

View File

@@ -73,33 +73,27 @@ namespace OpenSim.Framework
{
}
public InventoryFolderBase(UUID id)
public InventoryFolderBase(UUID id) : this()
{
ID = id;
}
public InventoryFolderBase(UUID id, UUID owner)
public InventoryFolderBase(UUID id, UUID owner) : this(id)
{
ID = id;
Owner = owner;
}
public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent)
public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent) : this(id, owner)
{
ID = id;
Name = name;
Owner = owner;
ParentID = parent;
}
public InventoryFolderBase(UUID id, string name, UUID owner, short type, UUID parent, ushort version)
public InventoryFolderBase(
UUID id, string name, UUID owner, short type, UUID parent, ushort version) : this(id, name, owner, parent)
{
ID = id;
Name = name;
Owner = owner;
Type = type;
ParentID = parent;
Version = version;
}
}
}
}

View File

@@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
sb.AppendFormat(
"OpenSim object memory churn : {0} MB/s\n",
"OpenSim last object memory churn : {0} MB/s\n",
Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
sb.AppendFormat(
"OpenSim average object memory churn : {0} MB/s\n",
Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
sb.AppendFormat(

View File

@@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring
private static bool m_enabled;
/// <summary>
/// Average memory churn in bytes per millisecond.
/// Last memory churn in bytes per millisecond.
/// </summary>
public static double AverageMemoryChurn
{
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
}
/// <summary>
/// Average memory churn in bytes per millisecond.
/// </summary>
public static double LastMemoryChurn
{
get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
}
/// <summary>
/// Maximum number of statistical samples.
/// </summary>

View File

@@ -355,10 +355,25 @@ 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);
Dictionary<string, Dictionary<string, Stat>> sceneStats;
if (StatsManager.TryGetStats("scene", out sceneStats))
{
foreach (KeyValuePair<string, Dictionary<string, Stat>> kvp in sceneStats)
{
foreach (Stat stat in kvp.Value.Values)
{
if (stat.Verbosity == StatVerbosity.Info)
{
sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName);
}
}
}
}
/*
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,24 @@ namespace OpenSim.Framework.Monitoring
/// </summary>
public class StatsManager
{
// Subcommand used to list other stats.
public const string AllSubCommand = "all";
// Subcommand used to list other stats.
public const string ListSubCommand = "list";
// All subcommands
public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
/// <summary>
/// Registered stats categorized by category/container/shortname
/// </summary>
/// <remarks>
/// Do not add or remove directly from this dictionary.
/// </remarks>
public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats
= new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>();
private static AssetStatsCollector assetStats;
private static UserStatsCollector userStats;
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
@@ -40,6 +61,75 @@ namespace OpenSim.Framework.Monitoring
public static UserStatsCollector UserStats { get { return userStats; } }
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
public static void RegisterConsoleCommands(ICommandConsole console)
{
console.Commands.AddCommand(
"General",
false,
"show stats",
"show stats [list|all|<category>]",
"Show statistical information for this server",
"If no final argument is specified then legacy statistics information is currently shown.\n"
+ "If list is specified then statistic categories are shown.\n"
+ "If all is specified then all registered statistics are shown.\n"
+ "If a category name is specified then only statistics from that category are shown.\n"
+ "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
HandleShowStatsCommand);
}
public static void HandleShowStatsCommand(string module, string[] cmd)
{
ICommandConsole con = MainConsole.Instance;
if (cmd.Length > 2)
{
var categoryName = cmd[2];
if (categoryName == AllSubCommand)
{
foreach (var category in RegisteredStats.Values)
{
OutputCategoryStatsToConsole(con, category);
}
}
else if (categoryName == ListSubCommand)
{
con.Output("Statistic categories available are:");
foreach (string category in RegisteredStats.Keys)
con.OutputFormat(" {0}", category);
}
else
{
Dictionary<string, Dictionary<string, Stat>> category;
if (!RegisteredStats.TryGetValue(categoryName, out category))
{
con.OutputFormat("No such category as {0}", categoryName);
}
else
{
OutputCategoryStatsToConsole(con, category);
}
}
}
else
{
// Legacy
con.Output(SimExtraStats.Report());
}
}
private static void OutputCategoryStatsToConsole(
ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category)
{
foreach (var container in category.Values)
{
foreach (Stat stat in container.Values)
{
con.Output(stat.ToConsoleString());
}
}
}
/// <summary>
/// Start collecting statistics related to assets.
/// Should only be called once.
@@ -61,5 +151,224 @@ namespace OpenSim.Framework.Monitoring
return userStats;
}
/// <summary>
/// Registers a statistic.
/// </summary>
/// <param name='stat'></param>
/// <returns></returns>
public static bool RegisterStat(Stat stat)
{
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
Dictionary<string, Stat> container = null, newContainer;
lock (RegisteredStats)
{
// Stat name is not unique across category/container/shortname key.
// 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.
if (TryGetStat(stat, out category, out container))
return false;
// We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
// This means that we don't need to lock or copy them on iteration, which will be a much more
// common operation after startup.
if (container != null)
newContainer = new Dictionary<string, Stat>(container);
else
newContainer = new Dictionary<string, Stat>();
if (category != null)
newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
else
newCategory = new Dictionary<string, Dictionary<string, Stat>>();
newContainer[stat.ShortName] = stat;
newCategory[stat.Container] = newContainer;
RegisteredStats[stat.Category] = newCategory;
}
return true;
}
/// <summary>
/// Deregister a statistic
/// </summary>>
/// <param name='stat'></param>
/// <returns></returns
public static bool DeregisterStat(Stat stat)
{
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
Dictionary<string, Stat> container = null, newContainer;
lock (RegisteredStats)
{
if (!TryGetStat(stat, out category, out container))
return false;
newContainer = new Dictionary<string, Stat>(container);
newContainer.Remove(stat.UniqueName);
newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
newCategory.Remove(stat.Container);
newCategory[stat.Container] = newContainer;
RegisteredStats[stat.Category] = newCategory;
return true;
}
}
public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats)
{
return RegisteredStats.TryGetValue(category, out stats);
}
public static bool TryGetStat(
Stat stat,
out Dictionary<string, Dictionary<string, Stat>> category,
out Dictionary<string, Stat> container)
{
category = null;
container = null;
lock (RegisteredStats)
{
if (RegisteredStats.TryGetValue(stat.Category, out category))
{
if (category.TryGetValue(stat.Container, out container))
{
if (container.ContainsKey(stat.ShortName))
return true;
}
}
}
return false;
}
}
/// <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 virtual string UnitName { get; private set; }
public virtual double Value { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param>
/// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param>
/// <param name='unitName'>
/// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value.
/// e.g. " frames"
/// </param>
/// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param>
/// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param>
/// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param>
/// <param name='description'>Description of stat</param>
public Stat(
string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description)
{
if (StatsManager.SubCommands.Contains(category))
throw new Exception(
string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
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);
}
public virtual string ToConsoleString()
{
return string.Format(
"{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName);
}
}
public class PercentageStat : Stat
{
public int Antecedent { get; set; }
public int Consequent { get; set; }
public override double Value
{
get
{
int c = Consequent;
// Avoid any chance of a multi-threaded divide-by-zero
if (c == 0)
return 0;
return (double)Antecedent / c * 100;
}
set
{
throw new Exception("Cannot set value on a PercentageStat");
}
}
public PercentageStat(
string shortName, string name, string category, string container, StatVerbosity verbosity, string description)
: base(shortName, name, "%", category, container, verbosity, description) {}
public override string ToConsoleString()
{
return string.Format(
"{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})",
Category, Container, ShortName, Value, UnitName, Antecedent, Consequent);
}
}
}

View File

@@ -89,6 +89,17 @@ namespace OpenSim.Framework.Monitoring
FirstTick = Environment.TickCount & Int32.MaxValue;
LastTick = FirstTick;
}
public ThreadWatchdogInfo(ThreadWatchdogInfo previousTwi)
{
Thread = previousTwi.Thread;
FirstTick = previousTwi.FirstTick;
LastTick = previousTwi.LastTick;
Timeout = previousTwi.Timeout;
IsTimedOut = previousTwi.IsTimedOut;
AlarmIfTimeout = previousTwi.AlarmIfTimeout;
AlarmMethod = previousTwi.AlarmMethod;
}
}
/// <summary>
@@ -97,6 +108,32 @@ namespace OpenSim.Framework.Monitoring
/// /summary>
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
/// <summary>
/// Is this watchdog active?
/// </summary>
public static bool Enabled
{
get { return m_enabled; }
set
{
// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value);
if (value == m_enabled)
return;
m_enabled = value;
if (m_enabled)
{
// Set now so we don't get alerted on the first run
LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue;
}
m_watchdogTimer.Enabled = m_enabled;
}
}
private static bool m_enabled;
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static Dictionary<int, ThreadWatchdogInfo> m_threads;
private static System.Timers.Timer m_watchdogTimer;
@@ -115,11 +152,6 @@ namespace OpenSim.Framework.Monitoring
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
m_watchdogTimer.AutoReset = false;
m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
// Set now so we don't get alerted on the first run
LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue;
m_watchdogTimer.Start();
}
/// <summary>
@@ -199,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
private static bool RemoveThread(int threadID)
{
lock (m_threads)
return m_threads.Remove(threadID);
{
ThreadWatchdogInfo twi;
if (m_threads.TryGetValue(threadID, out twi))
{
m_log.DebugFormat(
"[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
m_threads.Remove(threadID);
return true;
}
else
{
m_log.WarnFormat(
"[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
return false;
}
}
}
public static bool AbortThread(int threadID)
@@ -314,7 +364,9 @@ namespace OpenSim.Framework.Monitoring
if (callbackInfos == null)
callbackInfos = new List<ThreadWatchdogInfo>();
callbackInfos.Add(threadInfo);
// Send a copy of the watchdog info to prevent race conditions where the watchdog
// thread updates the monitoring info after an alarm has been sent out.
callbackInfos.Add(new ThreadWatchdogInfo(threadInfo));
}
}
}

76
OpenSim/Framework/Pool.cs Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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;
namespace OpenSim.Framework
{
/// <summary>
/// Naive pool implementation.
/// </summary>
/// <remarks>
/// Currently assumes that objects are in a useable state when returned.
/// </remarks>
public class Pool<T>
{
private Stack<T> m_pool;
private int m_maxPoolSize;
private Func<T> m_createFunction;
public Pool(Func<T> createFunction, int maxSize)
{
m_maxPoolSize = maxSize;
m_createFunction = createFunction;
m_pool = new Stack<T>(m_maxPoolSize);
}
public T GetObject()
{
lock (m_pool)
{
if (m_pool.Count > 0)
return m_pool.Pop();
else
return m_createFunction();
}
}
public void ReturnObject(T obj)
{
lock (m_pool)
{
if (m_pool.Count >= m_maxPoolSize)
return;
else
m_pool.Push(obj);
}
}
}
}

View File

@@ -65,9 +65,14 @@ namespace OpenSim.Framework.Serialization
UserAccount account = userService.GetUserAccount(UUID.Zero, userId);
if (account != null)
{
return MakeOspa(account.FirstName, account.LastName);
}
// else
// {
// m_log.WarnFormat("[OSP RESOLVER]: No user account for {0}", userId);
// System.Console.WriteLine("[OSP RESOLVER]: No user account for {0}", userId);
// }
return null;
}
@@ -79,10 +84,13 @@ namespace OpenSim.Framework.Serialization
/// <returns></returns>
public static string MakeOspa(string firstName, string lastName)
{
// m_log.DebugFormat("[OSP RESOLVER]: Making OSPA for {0} {1}", firstName, lastName);
string ospa
= OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
// m_log.DebugFormat("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
// System.Console.WriteLine("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
return
OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
return ospa;
}
/// <summary>

View File

@@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers
get { return m_httpServer; }
}
/// <summary>
/// Holds the non-viewer statistics collection object for this service/server
/// </summary>
protected IStatsCollector m_stats;
public BaseOpenSimServer()
{
m_startuptime = DateTime.Now;
@@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers
"show info",
"Show general information about the server", HandleShow);
m_console.Commands.AddCommand("General", false, "show stats",
"show stats",
"Show statistics", HandleShow);
m_console.Commands.AddCommand("General", false, "show threads",
"show threads",
"Show thread status", HandleShow);
@@ -226,12 +217,7 @@ namespace OpenSim.Framework.Servers
{
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
sb.Append(GetUptimeReport());
if (m_stats != null)
{
sb.Append(m_stats.Report());
}
sb.Append(StatsManager.SimExtraStats.Report());
sb.Append(Environment.NewLine);
sb.Append(GetThreadsReport());
@@ -382,10 +368,6 @@ namespace OpenSim.Framework.Servers
{
Notice("set log level [level] - change the console logging level only. For example, off or debug.");
Notice("show info - show server information (e.g. startup path).");
if (m_stats != null)
Notice("show stats - show statistical information for this server");
Notice("show threads - list tracked threads");
Notice("show uptime - show server startup time and uptime.");
Notice("show version - show server version.");
@@ -409,11 +391,6 @@ namespace OpenSim.Framework.Servers
ShowInfo();
break;
case "stats":
if (m_stats != null)
Notice(m_stats.Report());
break;
case "threads":
Notice(GetThreadsReport());
break;
@@ -604,8 +581,7 @@ namespace OpenSim.Framework.Servers
public string osSecret {
// Secret uuid for the simulator
get { return m_osSecret; }
get { return m_osSecret; }
}
public string StatReport(IOSHttpRequest httpRequest)
@@ -613,11 +589,11 @@ namespace OpenSim.Framework.Servers
// If we catch a request for "callback", wrap the response in the value for jsonp
if (httpRequest.Query.ContainsKey("callback"))
{
return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
}
else
{
return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
}
}

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);
@@ -643,14 +651,90 @@ namespace OpenSim.Framework.Servers.HttpServer
if (tickdiff > 3000)
{
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

@@ -30,7 +30,7 @@ namespace OpenSim
public class VersionInfo
{
private const string VERSION_NUMBER = "0.7.4";
private const Flavour VERSION_FLAVOUR = Flavour.Dev;
private const Flavour VERSION_FLAVOUR = Flavour.Extended;
public enum Flavour
{

View File

@@ -533,6 +533,19 @@ namespace OpenSim.Framework
return (x + y - (min >> 1) - (min >> 2) + (min >> 4));
}
/// <summary>
/// Determines whether a point is inside a bounding box.
/// </summary>
/// <param name='v'>/param>
/// <param name='min'></param>
/// <param name='max'></param>
/// <returns></returns>
public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max)
{
return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z
&& v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z;
}
/// <summary>
/// Are the co-ordinates of the new region visible from the old region?
/// </summary>
@@ -850,6 +863,12 @@ namespace OpenSim.Framework
return Math.Min(Math.Max(x, min), max);
}
public static Vector3 Clip(Vector3 vec, float min, float max)
{
return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max),
Clip(vec.Z, min, max));
}
/// <summary>
/// Convert an UUID to a raw uuid string. Right now this is a string without hyphens.
/// </summary>
@@ -1001,6 +1020,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];
@@ -695,13 +718,15 @@ namespace OpenSim.Framework
string requestUrl, TRequest obj, Action<TResponse> action)
{
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);
@@ -854,7 +879,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 +887,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 +913,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 +1002,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;
}
@@ -998,7 +1037,11 @@ namespace OpenSim.Framework
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest 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} SynchronousRestObject {1} {2}",
reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
@@ -1111,7 +1154,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,6 +1162,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);
}
return deserial;
}

View File

@@ -35,6 +35,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using log4net;
using NDesk.Options;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
@@ -253,8 +254,14 @@ namespace OpenSim
m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug);
m_console.Commands.AddCommand("Debug", false, "debug scene",
"debug scene <scripting> <collisions> <physics>",
"Turn on scene debugging", Debug);
"debug scene active|collisions|physics|scripting|teleport true|false",
"Turn on scene debugging.",
"If active is false then main scene update and maintenance loops are suspended.\n"
+ "If collisions is false then collisions with other objects are turned off.\n"
+ "If physics is false then all physics objects are non-physical.\n"
+ "If scripting is false then no scripting operations happen.\n"
+ "If teleport is true then some extra teleport debug information is logged.",
Debug);
m_console.Commands.AddCommand("General", false, "change region",
"change region <region name>",
@@ -310,8 +317,11 @@ namespace OpenSim
"Change the scale of a named prim", HandleEditScale);
m_console.Commands.AddCommand("Users", false, "kick user",
"kick user <first> <last> [message]",
"Kick a user off the simulator", KickUserCommand);
"kick user <first> <last> [--force] [message]",
"Kick a user off the simulator",
"The --force option will kick the user without any checks to see whether it's already in the process of closing\n"
+ "Only use this option if you are sure the avatar is inactive and a normal kick user operation does not removed them",
KickUserCommand);
m_console.Commands.AddCommand("Users", false, "show users",
"show users [full]",
@@ -328,10 +338,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);
@@ -453,11 +459,17 @@ namespace OpenSim
/// <param name="cmdparams">name of avatar to kick</param>
private void KickUserCommand(string module, string[] cmdparams)
{
if (cmdparams.Length < 4)
bool force = false;
OptionSet options = new OptionSet().Add("f|force", delegate (string v) { force = v != null; });
List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4)
return;
string alert = null;
if (cmdparams.Length > 4)
if (mainParams.Count > 4)
alert = String.Format("\n{0}\n", String.Join(" ", cmdparams, 4, cmdparams.Length - 4));
IList agents = SceneManager.GetCurrentSceneAvatars();
@@ -466,8 +478,8 @@ namespace OpenSim
{
RegionInfo regionInfo = presence.Scene.RegionInfo;
if (presence.Firstname.ToLower().Contains(cmdparams[2].ToLower()) &&
presence.Lastname.ToLower().Contains(cmdparams[3].ToLower()))
if (presence.Firstname.ToLower().Contains(mainParams[2].ToLower()) &&
presence.Lastname.ToLower().Contains(mainParams[3].ToLower()))
{
MainConsole.Instance.Output(
String.Format(
@@ -480,7 +492,7 @@ namespace OpenSim
else
presence.ControllingClient.Kick("\nThe OpenSim manager kicked you out.\n");
presence.Scene.IncomingCloseAgent(presence.UUID);
presence.Scene.IncomingCloseAgent(presence.UUID, force);
}
}
@@ -922,7 +934,8 @@ namespace OpenSim
}
else
{
MainConsole.Instance.Output("Usage: debug scene scripting|collisions|physics|teleport true|false");
MainConsole.Instance.Output(
"Usage: debug scene active|scripting|collisions|physics|teleport true|false");
}
break;
@@ -1002,33 +1015,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

@@ -223,8 +223,6 @@ namespace OpenSim
base.StartupSpecific();
m_stats = StatsManager.SimExtraStats;
// Create a ModuleLoader instance
m_moduleLoader = new ModuleLoader(m_config.Source);
@@ -234,51 +232,51 @@ namespace OpenSim
plugin.PostInitialise();
}
AddPluginCommands();
}
protected virtual void AddPluginCommands()
{
// If console exists add plugin commands.
if (m_console != null)
{
List<string> topics = GetHelpTopics();
StatsManager.RegisterConsoleCommands(m_console);
AddPluginCommands(m_console);
}
}
foreach (string topic in topics)
protected virtual void AddPluginCommands(CommandConsole console)
{
List<string> topics = GetHelpTopics();
foreach (string topic in topics)
{
string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
// This is a hack to allow the user to enter the help command in upper or lowercase. This will go
// away at some point.
console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
ICommander commander = null;
Scene s = SceneManager.CurrentOrFirstScene;
if (s != null && s.GetCommanders() != null)
{
string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
if (s.GetCommanders().ContainsKey(topic))
commander = s.GetCommanders()[topic];
}
// This is a hack to allow the user to enter the help command in upper or lowercase. This will go
// away at some point.
m_console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
m_console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
if (commander == null)
continue;
ICommander commander = null;
Scene s = SceneManager.CurrentOrFirstScene;
if (s != null && s.GetCommanders() != null)
{
if (s.GetCommanders().ContainsKey(topic))
commander = s.GetCommanders()[topic];
}
if (commander == null)
continue;
foreach (string command in commander.Commands.Keys)
{
m_console.Commands.AddCommand(capitalizedTopic, false,
topic + " " + command,
topic + " " + commander.Commands[command].ShortHelp(),
String.Empty, HandleCommanderCommand);
}
foreach (string command in commander.Commands.Keys)
{
console.Commands.AddCommand(capitalizedTopic, false,
topic + " " + command,
topic + " " + commander.Commands[command].ShortHelp(),
String.Empty, HandleCommanderCommand);
}
}
}
@@ -305,8 +303,13 @@ namespace OpenSim
m_httpServerPort = m_networkServersInfo.HttpListenerPort;
SceneManager.OnRestartSim += handleRestartRegion;
// Only start the memory watchdog once all regions are ready
SceneManager.OnRegionsReadyStatusChange += sm => MemoryWatchdog.Enabled = sm.AllRegionsReady;
// Only enable the watchdogs when all regions are ready. Otherwise we get false positives when cpu is
// heavily used during initial startup.
//
// FIXME: It's also possible that region ready status should be flipped during an OAR load since this
// also makes heavy use of the CPU.
SceneManager.OnRegionsReadyStatusChange
+= sm => { MemoryWatchdog.Enabled = sm.AllRegionsReady; Watchdog.Enabled = sm.AllRegionsReady; };
}
/// <summary>

View File

@@ -163,8 +163,8 @@ namespace OpenSim.Region.ClientStack.Linden
m_HostCapsObj.RegisterHandler(
"SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
m_log.DebugFormat(
"[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
// m_log.DebugFormat(
// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
//m_capsHandlers["MapLayer"] =
// new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST",
@@ -254,11 +254,12 @@ namespace OpenSim.Region.ClientStack.Linden
public string SeedCapRequest(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName);
m_log.DebugFormat(
"[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
{
m_log.DebugFormat(
m_log.WarnFormat(
"[CAPS]: Unauthorized CAPS client {0} from {1}",
m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);

View File

@@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden
//scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack);
scene.EventManager.OnNewClient += OnNewClient;
// scene.EventManager.OnNewClient += OnNewClient;
// TODO: Leaving these open, or closing them when we
// become a child is incorrect. It messes up TP in a big
@@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden
// circuit is there.
scene.EventManager.OnClientClosed += ClientClosed;
scene.EventManager.OnMakeChildAgent += MakeChildAgent;
scene.EventManager.OnRegisterCaps += OnRegisterCaps;
@@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden
false,
"debug eq",
"debug eq [0|1|2]",
"Turn on event queue debugging"
+ "<= 0 - turns off all event queue logging"
+ ">= 1 - turns on outgoing event logging"
+ ">= 2 - turns on poll notification",
"Turn on event queue debugging\n"
+ " <= 0 - turns off all event queue logging\n"
+ " >= 1 - turns on outgoing event logging\n"
+ " >= 2 - turns on poll notification",
HandleDebugEq);
}
else
@@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion
private void OnNewClient(IClientAPI client)
{
//client.OnLogout += ClientClosed;
}
// private void ClientClosed(IClientAPI client)
// {
// ClientClosed(client.AgentId);
// }
private void ClientClosed(UUID agentID, Scene scene)
{
// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);

View File

@@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
UUID spId = TestHelpers.ParseTail(0x1);
SceneHelpers.AddScenePresence(m_scene, spId);
m_scene.IncomingCloseAgent(spId);
m_scene.IncomingCloseAgent(spId, false);
// TODO: Add more assertions for the other aspects of event queues
Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0));

View File

@@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public Packet Packet;
/// <summary>
/// Default constructor
/// No arg constructor.
/// </summary>
public IncomingPacket() {}
/// <summary>
/// Constructor
/// </summary>
/// <param name="client">Reference to the client this packet came from</param>
/// <param name="packet">Packet data</param>

View File

@@ -91,8 +91,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public event ObjectDeselect OnObjectDetach;
public event ObjectDrop OnObjectDrop;
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
/// <summary>
/// Called when an AgentUpdate message is received and before OnAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object is reused for subsequent AgentUpdates.
/// </remarks>
public event UpdateAgent OnPreAgentUpdate;
/// <summary>
/// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object is reused for subsequent AgentUpdates.
/// </remarks>
public event UpdateAgent OnAgentUpdate;
public event AgentRequestSit OnAgentRequestSit;
public event AgentSit OnAgentSit;
public event AvatarPickerRequest OnAvatarPickerRequest;
@@ -346,7 +361,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_moneyBalance;
private int m_animationSequenceNumber = 1;
private bool m_SendLogoutPacketWhenClosing = true;
private AgentUpdateArgs lastarg;
/// <summary>
/// We retain a single AgentUpdateArgs so that we can constantly reuse it rather than construct a new one for
/// every single incoming AgentUpdate. Every client sends 10 AgentUpdate UDP messages per second, even if it
/// is doing absolutely nothing.
/// </summary>
/// <remarks>
/// This does mean that agent updates must be processed synchronously, at least for each client, and called methods
/// cannot retain a reference to it outside of that method.
/// </remarks>
private AgentUpdateArgs m_lastAgentUpdateArgs;
protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@@ -487,16 +512,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Client Methods
/// <summary>
/// Close down the client view
/// </summary>
public void Close()
{
Close(false);
}
public void Close(bool force)
{
// We lock here to prevent race conditions between two threads calling close simultaneously (e.g.
// a simultaneous relog just as a client is being closed out due to no packet ack from the old connection.
lock (CloseSyncLock)
{
if (!IsActive)
// We still perform a force close inside the sync lock since this is intended to attempt close where
// there is some unidentified connection problem, not where we have issues due to deadlock
if (!IsActive && !force)
return;
IsActive = false;
@@ -3917,7 +3946,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -3962,7 +3993,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -4447,37 +4481,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (bl[i].BannedUserID == UUID.Zero)
continue;
BannedUsers.Add(bl[i].BannedUserID);
if (BannedUsers.Count >= 50 || (i == (bl.Length - 1) && BannedUsers.Count > 0))
{
EstateOwnerMessagePacket packet = new EstateOwnerMessagePacket();
packet.AgentData.TransactionID = UUID.Random();
packet.AgentData.AgentID = AgentId;
packet.AgentData.SessionID = SessionId;
packet.MethodData.Invoice = invoice;
packet.MethodData.Method = Utils.StringToBytes("setaccess");
EstateOwnerMessagePacket.ParamListBlock[] returnblock = new EstateOwnerMessagePacket.ParamListBlock[6 + BannedUsers.Count];
int j;
for (j = 0; j < (6 + BannedUsers.Count); j++)
{
returnblock[j] = new EstateOwnerMessagePacket.ParamListBlock();
}
j = 0;
returnblock[j].Parameter = Utils.StringToBytes(estateID.ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes(((int)Constants.EstateAccessCodex.EstateBans).ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
returnblock[j].Parameter = Utils.StringToBytes(BannedUsers.Count.ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
foreach (UUID banned in BannedUsers)
{
returnblock[j].Parameter = banned.GetBytes(); j++;
}
packet.ParamList = returnblock;
packet.Header.Reliable = true;
OutPacket(packet, ThrottleOutPacketType.Task);
BannedUsers.Clear();
}
}
EstateOwnerMessagePacket packet = new EstateOwnerMessagePacket();
packet.AgentData.TransactionID = UUID.Random();
packet.AgentData.AgentID = AgentId;
packet.AgentData.SessionID = SessionId;
packet.MethodData.Invoice = invoice;
packet.MethodData.Method = Utils.StringToBytes("setaccess");
EstateOwnerMessagePacket.ParamListBlock[] returnblock = new EstateOwnerMessagePacket.ParamListBlock[6 + BannedUsers.Count];
for (int i = 0; i < (6 + BannedUsers.Count); i++)
{
returnblock[i] = new EstateOwnerMessagePacket.ParamListBlock();
}
int j = 0;
returnblock[j].Parameter = Utils.StringToBytes(estateID.ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes(((int)Constants.EstateAccessCodex.EstateBans).ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
returnblock[j].Parameter = Utils.StringToBytes(BannedUsers.Count.ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes("0"); j++;
foreach (UUID banned in BannedUsers)
{
returnblock[j].Parameter = banned.GetBytes(); j++;
}
packet.ParamList = returnblock;
packet.Header.Reliable = false;
OutPacket(packet, ThrottleOutPacketType.Task);
}
public void SendRegionInfoToEstateMenu(RegionInfoForEstateMenuArgs args)
@@ -4947,7 +4988,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2;
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2;
ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock();
ImprovedTerseObjectUpdatePacket.ObjectDataBlock block
= PacketPool.Instance.GetDataBlock<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
block.Data = data;
if (textureEntry != null && textureEntry.Length > 0)
@@ -5179,7 +5222,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected virtual void RegisterLocalPacketHandlers()
{
AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout);
// If AgentUpdate is ever handled asynchronously, then we will also need to construct a new AgentUpdateArgs
// for each AgentUpdate packet.
AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false);
AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false);
AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false);
AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false);
@@ -5406,80 +5453,83 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Scene/Avatar
private bool HandleAgentUpdate(IClientAPI sener, Packet Pack)
private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
{
if (OnAgentUpdate != null)
{
bool update = false;
AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
#region Packet Session and User Check
if (agenUpdate.AgentData.SessionID != SessionId || agenUpdate.AgentData.AgentID != AgentId)
if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId)
{
PacketPool.Instance.ReturnPacket(packet);
return false;
}
#endregion
AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData;
bool update = false;
AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
// We can only check when we have something to check
// against.
if (lastarg != null)
if (m_lastAgentUpdateArgs != null)
{
// These should be ordered from most-likely to
// least likely to change. I've made an initial
// guess at that.
update =
(
(x.BodyRotation != lastarg.BodyRotation) ||
(x.CameraAtAxis != lastarg.CameraAtAxis) ||
(x.CameraCenter != lastarg.CameraCenter) ||
(x.CameraLeftAxis != lastarg.CameraLeftAxis) ||
(x.CameraUpAxis != lastarg.CameraUpAxis) ||
(x.ControlFlags != lastarg.ControlFlags) ||
(x.Far != lastarg.Far) ||
(x.Flags != lastarg.Flags) ||
(x.State != lastarg.State) ||
(x.HeadRotation != lastarg.HeadRotation) ||
(x.SessionID != lastarg.SessionID) ||
(x.AgentID != lastarg.AgentID)
(x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
(x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
(x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
(x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
(x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
(x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
(x.Far != m_lastAgentUpdateArgs.Far) ||
(x.Flags != m_lastAgentUpdateArgs.Flags) ||
(x.State != m_lastAgentUpdateArgs.State) ||
(x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
(x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
(x.AgentID != m_lastAgentUpdateArgs.AgentID)
);
}
else
{
m_lastAgentUpdateArgs = new AgentUpdateArgs();
update = true;
}
// These should be ordered from most-likely to
// least likely to change. I've made an initial
// guess at that.
if (update)
{
// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name);
AgentUpdateArgs arg = new AgentUpdateArgs();
arg.AgentID = x.AgentID;
arg.BodyRotation = x.BodyRotation;
arg.CameraAtAxis = x.CameraAtAxis;
arg.CameraCenter = x.CameraCenter;
arg.CameraLeftAxis = x.CameraLeftAxis;
arg.CameraUpAxis = x.CameraUpAxis;
arg.ControlFlags = x.ControlFlags;
arg.Far = x.Far;
arg.Flags = x.Flags;
arg.HeadRotation = x.HeadRotation;
arg.SessionID = x.SessionID;
arg.State = x.State;
m_lastAgentUpdateArgs.AgentID = x.AgentID;
m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation;
m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter;
m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags;
m_lastAgentUpdateArgs.Far = x.Far;
m_lastAgentUpdateArgs.Flags = x.Flags;
m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation;
m_lastAgentUpdateArgs.SessionID = x.SessionID;
m_lastAgentUpdateArgs.State = x.State;
UpdateAgent handlerAgentUpdate = OnAgentUpdate;
UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
lastarg = arg; // save this set of arguments for nexttime
if (handlerPreAgentUpdate != null)
OnPreAgentUpdate(this, arg);
OnPreAgentUpdate(this, m_lastAgentUpdateArgs);
if (handlerAgentUpdate != null)
OnAgentUpdate(this, arg);
OnAgentUpdate(this, m_lastAgentUpdateArgs);
handlerAgentUpdate = null;
handlerPreAgentUpdate = null;
}
}
PacketPool.Instance.ReturnPacket(packet);
return true;
}
@@ -5810,7 +5860,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
args.Channel = ch;
args.From = String.Empty;
args.Message = Utils.BytesToString(msg);
args.Type = ChatTypeEnum.Shout;
args.Type = ChatTypeEnum.Region; //Behaviour in SL is that the response can be heard from any distance
args.Position = new Vector3();
args.Scene = Scene;
args.Sender = this;
@@ -9043,7 +9093,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
#endregion
switch (Utils.BytesToString(messagePacket.MethodData.Method))
string method = Utils.BytesToString(messagePacket.MethodData.Method);
switch (method)
{
case "getinfo":
if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false))
@@ -9359,7 +9411,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return true;
default:
m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket);
m_log.WarnFormat(
"[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}",
method, Name, Scene.Name);
for (int i = 0; i < messagePacket.ParamList.Length; i++)
{
EstateOwnerMessagePacket.ParamListBlock block = messagePacket.ParamList[i];
string data = (string)Utils.BytesToString(block.Parameter);
m_log.DebugFormat("[LLCLIENTVIEW]: Param {0}={1}", i, data);
}
return true;
}
@@ -11745,7 +11807,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
logPacket = false;
if (DebugPacketLevel <= 50
& (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
&& (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
logPacket = false;
if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
@@ -11819,8 +11881,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (!ProcessPacketMethod(packet))
m_log.Warn("[CLIENT]: unhandled packet " + packet.Type);
PacketPool.Instance.ReturnPacket(packet);
}
private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
@@ -11989,7 +12049,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
Kick(reason);
Thread.Sleep(1000);
Close();
Disconnect();
}
public void Disconnect()
@@ -12273,7 +12333,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f);
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];

View File

@@ -37,6 +37,7 @@ using log4net;
using Nini.Config;
using OpenMetaverse.Packets;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
@@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>The measured resolution of Environment.TickCount</summary>
public readonly float TickCountResolution;
/// <summary>Number of prim updates to put on the queue each time the
/// OnQueueEmpty event is triggered for updates</summary>
public readonly int PrimUpdatesPerCallback;
/// <summary>Number of texture packets to put on the queue each time the
/// OnQueueEmpty event is triggered for textures</summary>
public readonly int TextureSendLimit;
@@ -111,6 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//PacketEventDictionary packetEvents = new PacketEventDictionary();
/// <summary>Incoming packets that are awaiting handling</summary>
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
/// <summary></summary>
//private UDPClientCollection m_clients = new UDPClientCollection();
/// <summary>Bandwidth throttle for this UDP server</summary>
@@ -121,28 +125,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Manages authentication for agent circuits</summary>
private AgentCircuitManager m_circuitManager;
/// <summary>Reference to the scene this UDP server is attached to</summary>
protected Scene m_scene;
/// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
private Location m_location;
/// <summary>The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default</summary>
private int m_recvBufferSize;
/// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling;
/// <summary>Tracks whether or not a packet was sent each round so we know
/// whether or not to sleep</summary>
private bool m_packetSent;
/// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
private int m_elapsedMSSinceLastStatReport = 0;
/// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
private int m_tickLastOutgoingPacketHandler;
/// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
private int m_elapsedMSOutgoingPacketHandler;
/// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed100MSOutgoingPacketHandler;
/// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed500MSOutgoingPacketHandler;
@@ -155,6 +168,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Flag to signal when clients should send pings</summary>
protected bool m_sendPing;
private Pool<IncomingPacket> m_incomingPacketPool;
private int m_defaultRTO = 0;
private int m_maxRTO = 0;
private int m_ackTimeout = 0;
@@ -175,7 +190,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
private IClientAPI m_currentIncomingClient;
public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
public LLUDPServer(
IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port)
{
#region Environment.TickCount Measurement
@@ -222,6 +239,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_pausedAckTimeout = 1000 * 300; // 5 minutes
}
// FIXME: This actually only needs to be done once since the PacketPool is shared across all servers.
// However, there is no harm in temporarily doing it multiple times.
IConfig packetConfig = configSource.Configs["PacketPool"];
if (packetConfig != null)
{
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false);
}
#region BinaryStats
config = configSource.Configs["Statistics.Binary"];
m_shouldCollectStats = false;
@@ -249,20 +276,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_throttle = new TokenBucket(null, sceneThrottleBps);
ThrottleRates = new ThrottleRates(configSource);
if (UsePools)
m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
}
public void Start()
{
if (m_scene == null)
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
StartInbound();
StartOutbound();
m_elapsedMSSinceLastStatReport = Environment.TickCount;
}
private void StartInbound()
{
m_log.InfoFormat(
"[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
m_asyncPacketHandling ? "asynchronous" : "synchronous");
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
base.Start(m_recvBufferSize, m_asyncPacketHandling);
base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
// Start the packet processing threads
// This thread will process the packets received that are placed on the packetInbox
Watchdog.StartThread(
IncomingPacketHandler,
string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
@@ -271,6 +306,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true,
GetWatchdogIncomingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
private new void StartOutbound()
{
m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
base.StartOutbound();
Watchdog.StartThread(
OutgoingPacketHandler,
@@ -280,8 +322,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true,
GetWatchdogOutgoingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
m_elapsedMSSinceLastStatReport = Environment.TickCount;
public void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
base.StopOutbound();
base.StopInbound();
}
/// <summary>
@@ -306,12 +353,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
}
public new void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
base.Stop();
}
public void AddScene(IScene scene)
{
if (m_scene != null)
@@ -328,6 +369,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_scene = (Scene)scene;
m_location = new Location(m_scene.RegionInfo.RegionHandle);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp start",
"debug lludp start <in|out|all>",
"Control LLUDP packet processing.",
"No effect if packet processing has already started.\n"
+ "in - start inbound processing.\n"
+ "out - start outbound processing.\n"
+ "all - start in and outbound processing.\n",
HandleStartCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp stop",
"debug lludp stop <in|out|all>",
"Stop LLUDP packet processing.",
"No effect if packet processing has already stopped.\n"
+ "in - stop inbound processing.\n"
+ "out - stop outbound processing.\n"
+ "all - stop in and outbound processing.\n",
HandleStopCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp status",
"debug lludp status",
"Return status of LLUDP packet processing.",
HandleStatusCommand);
}
private void HandleStartCommand(string module, string[] args)
{
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
StartInbound();
if (subCommand == "out" || subCommand == "all")
StartOutbound();
}
private void HandleStopCommand(string module, string[] args)
{
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
StopInbound();
if (subCommand == "out" || subCommand == "all")
StopOutbound();
}
private void HandleStatusCommand(string module, string[] args)
{
MainConsole.Instance.OutputFormat(
"IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat(
"OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled");
}
public bool HandlesRegion(Location x)
@@ -411,6 +527,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
byte[] data = packet.ToBytes();
SendPacketData(udpClient, data, packet.Type, category, method);
}
PacketPool.Instance.ReturnPacket(packet);
}
/// <summary>
@@ -695,7 +813,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LLUDPClient udpClient = null;
Packet packet = null;
int packetEnd = buffer.DataLength - 1;
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
#region Decoding
@@ -705,7 +823,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
return; // Drop undersizd packet
return; // Drop undersized packet
}
int headerLen = 7;
@@ -728,7 +846,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
try
{
packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
// packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
// // Only allocate a buffer for zerodecoding if the packet is zerocoded
// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
// If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
// assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
// bytes are copied out).
packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
// Only allocate a buffer for zerodecoding if the packet is zerocoded
((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
}
@@ -743,11 +867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return; // Drop short packet
}
catch(Exception e)
catch (Exception e)
{
if (m_malformedCount < 100)
m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
m_malformedCount++;
if ((m_malformedCount % 100000) == 0)
m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount);
}
@@ -768,7 +894,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode)
{
object[] array = new object[] { buffer, packet };
// We need to copy the endpoint so that it doesn't get changed when another thread reuses the
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleUseCircuitCode, array);
@@ -777,7 +905,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Determine which agent this packet came from
IClientAPI client;
if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView))
if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
{
//m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
return;
@@ -801,6 +929,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Handle appended ACKs
if (packet.Header.AppendedAcks && packet.Header.AckList != null)
{
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
// packet.Header.AckList.Length, client.Name, m_scene.Name);
for (int i = 0; i < packet.Header.AckList.Length; i++)
udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
}
@@ -810,6 +942,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
PacketAckPacket ackPacket = (PacketAckPacket)packet;
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
// ackPacket.Packets.Length, client.Name, m_scene.Name);
for (int i = 0; i < ackPacket.Packets.Length; i++)
udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
@@ -823,6 +959,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Header.Reliable)
{
// m_log.DebugFormat(
// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
// This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
@@ -869,6 +1009,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type == PacketType.StartPingCheck)
{
// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
// We don't need to do anything else with ping checks
StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
CompletePing(udpClient, startPing.PingID.PingID);
@@ -888,8 +1030,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Ping Check Handling
IncomingPacket incomingPacket;
// Inbox insertion
packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet));
if (UsePools)
{
incomingPacket = m_incomingPacketPool.GetObject();
incomingPacket.Client = (LLClientView)client;
incomingPacket.Packet = packet;
}
else
{
incomingPacket = new IncomingPacket((LLClientView)client, packet);
}
packetInbox.Enqueue(incomingPacket);
}
#region BinaryStats
@@ -975,21 +1130,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void HandleUseCircuitCode(object o)
{
IPEndPoint remoteEndPoint = null;
IPEndPoint endPoint = null;
IClientAPI client = null;
try
{
// DateTime startTime = DateTime.Now;
object[] array = (object[])o;
UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
endPoint = (IPEndPoint)array[0];
UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
m_log.DebugFormat(
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
AuthenticateResponse sessionInfo;
if (IsClientAuthorized(uccp, out sessionInfo))
@@ -1000,13 +1153,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
uccp.CircuitCode.Code,
uccp.CircuitCode.ID,
uccp.CircuitCode.SessionID,
remoteEndPoint,
endPoint,
sessionInfo);
// Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious.
SendAckImmediate(remoteEndPoint, uccp.Header.Sequence);
SendAckImmediate(endPoint, uccp.Header.Sequence);
// We only want to send initial data to new clients, not ones which are being converted from child to root.
if (client != null)
@@ -1017,7 +1170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Don't create clients for unauthorized requesters.
m_log.WarnFormat(
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
}
// m_log.DebugFormat(
@@ -1029,7 +1182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
m_log.ErrorFormat(
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a",
endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown",
e.Message,
@@ -1094,20 +1247,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IClientAPI client = null;
// In priciple there shouldn't be more than one thread here, ever.
// But in case that happens, we need to synchronize this piece of code
// because it's too important
lock (this)
// We currently synchronize this code across the whole scene to avoid issues such as
// http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
// consistently, this lock could probably be removed.
lock (this)
{
if (!m_scene.TryGetClient(agentID, out client))
{
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
client.OnLogout += LogoutHandler;
((LLClientView)client).DisableFacelights = m_disableFacelights;
client.Start();
}
}
@@ -1146,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// on to en-US to avoid number parsing issues
Culture.SetCurrentCulture();
while (base.IsRunning)
while (IsRunningInbound)
{
try
{
@@ -1161,7 +1314,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
if (packetInbox.Dequeue(100, ref incomingPacket))
{
ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
if (UsePools)
m_incomingPacketPool.ReturnObject(incomingPacket);
}
}
catch (Exception ex)
{
@@ -1188,7 +1346,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Action generic every round
Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
while (base.IsRunning)
while (base.IsRunningOutbound)
{
try
{

View File

@@ -30,6 +30,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using log4net;
using OpenSim.Framework;
namespace OpenMetaverse
{
@@ -58,17 +59,29 @@ namespace OpenMetaverse
/// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling;
/// <summary>The all important shutdown flag</summary>
private volatile bool m_shutdownFlag = true;
/// <summary>
/// Pool to use for handling data. May be null if UsePools = false;
/// </summary>
protected OpenSim.Framework.Pool<UDPPacketBuffer> m_pool;
/// <summary>Returns true if the server is currently listening, otherwise false</summary>
public bool IsRunning { get { return !m_shutdownFlag; } }
/// <summary>
/// Are we to use object pool(s) to reduce memory churn when receiving data?
/// </summary>
public bool UsePools { get; protected set; }
/// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary>
public bool IsRunningInbound { get; private set; }
/// <summary>Returns true if the server is currently sending outbound packets, otherwise false</summary>
/// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks>
public bool IsRunningOutbound { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="bindAddress">Local IP address to bind the server to</param>
/// <param name="port">Port to listening for incoming UDP packets on</param>
/// /// <param name="usePool">Are we to use an object pool to get objects for handing inbound data?</param>
public OpenSimUDPBase(IPAddress bindAddress, int port)
{
m_localBindAddress = bindAddress;
@@ -76,7 +89,7 @@ namespace OpenMetaverse
}
/// <summary>
/// Start the UDP server
/// Start inbound UDP packet handling.
/// </summary>
/// <param name="recvBufferSize">The size of the receive buffer for
/// the UDP socket. This value is passed up to the operating system
@@ -91,11 +104,16 @@ namespace OpenMetaverse
/// manner (not throwing an exception when the remote side resets the
/// connection). This call is ignored on Mono where the flag is not
/// necessary</remarks>
public void Start(int recvBufferSize, bool asyncPacketHandling)
public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
{
if (UsePools)
m_pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500);
else
m_pool = null;
m_asyncPacketHandling = asyncPacketHandling;
if (m_shutdownFlag)
if (!IsRunningInbound)
{
const int SIO_UDP_CONNRESET = -1744830452;
@@ -127,8 +145,7 @@ namespace OpenMetaverse
m_udpSocket.Bind(ipep);
// we're not shutting down, we're starting up
m_shutdownFlag = false;
IsRunningInbound = true;
// kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in
@@ -138,28 +155,41 @@ namespace OpenMetaverse
}
/// <summary>
/// Stops the UDP server
/// Start outbound UDP packet handling.
/// </summary>
public void Stop()
public void StartOutbound()
{
if (!m_shutdownFlag)
IsRunningOutbound = true;
}
public void StopInbound()
{
if (IsRunningInbound)
{
// wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set shutdownFlag to inform the other
// threads. Once we have the lock, we set IsRunningInbound = false to inform the other
// threads that the socket is closed.
m_shutdownFlag = true;
IsRunningInbound = false;
m_udpSocket.Close();
}
}
public void StopOutbound()
{
IsRunningOutbound = false;
}
private void AsyncBeginReceive()
{
// allocate a packet buffer
//WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
UDPPacketBuffer buf = new UDPPacketBuffer();
UDPPacketBuffer buf;
if (!m_shutdownFlag)
if (UsePools)
buf = m_pool.GetObject();
else
buf = new UDPPacketBuffer();
if (IsRunningInbound)
{
try
{
@@ -212,7 +242,7 @@ namespace OpenMetaverse
{
// Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive
if (!m_shutdownFlag)
if (IsRunningInbound)
{
// Asynchronous mode will start another receive before the
// callback for this packet is even fired. Very parallel :-)
@@ -221,8 +251,6 @@ namespace OpenMetaverse
// get the buffer that was created in AsyncBeginReceive
// this is the received data
//WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
//UDPPacketBuffer buffer = wrappedBuffer.Instance;
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
@@ -239,7 +267,8 @@ namespace OpenMetaverse
catch (ObjectDisposedException) { }
finally
{
//wrappedBuffer.Dispose();
if (UsePools)
m_pool.ReturnObject(buffer);
// Synchronous mode waits until the packet callback completes
// before starting the receive to fetch another packet
@@ -252,7 +281,7 @@ namespace OpenMetaverse
public void AsyncBeginSend(UDPPacketBuffer buf)
{
if (!m_shutdownFlag)
if (IsRunningOutbound)
{
try
{

View File

@@ -31,10 +31,10 @@ using System.Reflection;
using OpenMetaverse;
using OpenMetaverse.Packets;
using log4net;
using OpenSim.Framework.Monitoring;
namespace OpenSim.Framework
namespace OpenSim.Region.ClientStack.LindenUDP
{
public sealed class PacketPool
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -44,14 +44,28 @@ namespace OpenSim.Framework
private bool packetPoolEnabled = true;
private bool dataBlockPoolEnabled = true;
private PercentageStat m_packetsReusedStat = new PercentageStat(
"PacketsReused",
"Packets reused",
"clientstack",
"packetpool",
StatVerbosity.Debug,
"Number of packets reused out of all requests to the packet pool");
private PercentageStat m_blocksReusedStat = new PercentageStat(
"BlocksReused",
"Blocks reused",
"clientstack",
"packetpool",
StatVerbosity.Debug,
"Number of data blocks reused out of all requests to the packet pool");
/// <summary>
/// Pool of packets available for reuse.
/// </summary>
private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>();
private static Dictionary<Type, Stack<Object>> DataBlocks =
new Dictionary<Type, Stack<Object>>();
static PacketPool()
{
}
private static Dictionary<Type, Stack<Object>> DataBlocks = new Dictionary<Type, Stack<Object>>();
public static PacketPool Instance
{
@@ -70,8 +84,21 @@ namespace OpenSim.Framework
get { return dataBlockPoolEnabled; }
}
private PacketPool()
{
StatsManager.RegisterStat(m_packetsReusedStat);
StatsManager.RegisterStat(m_blocksReusedStat);
}
/// <summary>
/// Gets a packet of the given type.
/// </summary>
/// <param name='type'></param>
/// <returns>Guaranteed to always return a packet, whether from the pool or newly constructed.</returns>
public Packet GetPacket(PacketType type)
{
m_packetsReusedStat.Consequent++;
Packet packet;
if (!packetPoolEnabled)
@@ -81,13 +108,19 @@ namespace OpenSim.Framework
{
if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0)
{
// m_log.DebugFormat("[PACKETPOOL]: Building {0} packet", type);
// Creating a new packet if we cannot reuse an old package
packet = Packet.BuildPacket(type);
}
else
{
// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type);
// Recycle old packages
packet = (pool[type]).Pop();
m_packetsReusedStat.Antecedent++;
packet = pool[type].Pop();
}
}
@@ -136,7 +169,7 @@ namespace OpenSim.Framework
{
PacketType type = GetType(bytes);
Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
// Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
int i = 0;
Packet packet = GetPacket(type);
@@ -183,6 +216,7 @@ namespace OpenSim.Framework
switch (packet.Type)
{
// List pooling packets here
case PacketType.AgentUpdate:
case PacketType.PacketAck:
case PacketType.ObjectUpdate:
case PacketType.ImprovedTerseObjectUpdate:
@@ -197,7 +231,9 @@ namespace OpenSim.Framework
if ((pool[type]).Count < 50)
{
(pool[type]).Push(packet);
// m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type);
pool[type].Push(packet);
}
}
break;
@@ -209,16 +245,21 @@ namespace OpenSim.Framework
}
}
public static T GetDataBlock<T>() where T: new()
public T GetDataBlock<T>() where T: new()
{
lock (DataBlocks)
{
m_blocksReusedStat.Consequent++;
Stack<Object> s;
if (DataBlocks.TryGetValue(typeof(T), out s))
{
if (s.Count > 0)
{
m_blocksReusedStat.Antecedent++;
return (T)s.Pop();
}
}
else
{
@@ -229,7 +270,7 @@ namespace OpenSim.Framework
}
}
public static void ReturnDataBlock<T>(T block) where T: new()
public void ReturnDataBlock<T>(T block) where T: new()
{
if (block == null)
return;
@@ -244,4 +285,4 @@ namespace OpenSim.Framework
}
}
}
}
}

View File

@@ -43,7 +43,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
/// This will contain basic tests for the LindenUDP client stack
/// </summary>
[TestFixture]
public class BasicCircuitTests
public class BasicCircuitTests : OpenSimTestCase
{
private Scene m_scene;
private TestLLUDPServer m_udpServer;
@@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
}
[SetUp]
public void SetUp()
public override void SetUp()
{
base.SetUp();
m_scene = new SceneHelpers().SetupScene();
}
@@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
public void TestAddClient()
{
TestHelpers.InMethod();
// XmlConfigurator.Configure();
// TestHelpers.EnableLogging();
AddUdpServer();

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

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

@@ -461,7 +461,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup rezzedAtt = presence.GetAttachments()[0];
scene.IncomingCloseAgent(presence.UUID);
scene.IncomingCloseAgent(presence.UUID, false);
// Check that we can't retrieve this attachment from the scene.
Assert.That(scene.GetSceneObjectGroup(rezzedAtt.UUID), Is.Null);
@@ -641,4 +641,4 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
// Assert.That(presence.HasAttachments(), Is.True, "Presence has not received new objects");
// }
}
}
}

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

@@ -350,38 +350,38 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL1.PrincipalID));
}
/// <summary>
/// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where
/// an account exists with the same name as the creator, though not the same id.
/// </summary>
[Test]
public void TestLoadIarV0_1SameNameCreator()
{
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood");
UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire");
m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream);
InventoryItemBase foundItem1
= InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name);
Assert.That(
foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()),
"Loaded item non-uuid creator doesn't match original");
Assert.That(
foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID),
"Loaded item uuid creator doesn't match original");
Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID),
"Loaded item owner doesn't match inventory reciever");
AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString());
string xmlData = Utils.BytesToString(asset1.Data);
SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID));
}
// /// <summary>
// /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where
// /// an account exists with the same name as the creator, though not the same id.
// /// </summary>
// [Test]
// public void TestLoadIarV0_1SameNameCreator()
// {
// TestHelpers.InMethod();
// TestHelpers.EnableLogging();
//
// UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood");
// UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire");
//
// m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream);
// InventoryItemBase foundItem1
// = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name);
//
// Assert.That(
// foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()),
// "Loaded item non-uuid creator doesn't match original");
// Assert.That(
// foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID),
// "Loaded item uuid creator doesn't match original");
// Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID),
// "Loaded item owner doesn't match inventory reciever");
//
// AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString());
// string xmlData = Utils.BytesToString(asset1.Data);
// SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
//
// Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID));
// }
/// <summary>
/// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where

View File

@@ -471,6 +471,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (sp.ParentID != (uint)0)
sp.StandUp();
// At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to
// the viewer. However, it might mean that the viewer does not see the black teleport screen (untested).
sp.ControllingClient.SendTeleportStart(teleportFlags);
// the avatar.Close below will clear the child region list. We need this below for (possibly)
@@ -546,8 +548,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// So let's wait
Thread.Sleep(200);
// At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
// unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
// only on TeleportFinish). This is untested for region teleport between different simulators
// though this probably also works.
m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
}
else
{
@@ -644,7 +649,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// an agent cannot teleport back to this region if it has teleported away.
Thread.Sleep(2000);
sp.Scene.IncomingCloseAgent(sp.UUID);
sp.Scene.IncomingCloseAgent(sp.UUID, false);
}
else
{

View File

@@ -40,7 +40,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
protected string m_assetServerURL;
protected HGAssetMapper m_assetMapper;
public HGUuidGatherer(HGAssetMapper assMap, IAssetService assetCache, string assetServerURL) : base(assetCache)
public HGUuidGatherer(HGAssetMapper assMap, IAssetService assetService, string assetServerURL) : base(assetService)
{
m_assetMapper = assMap;
m_assetServerURL = assetServerURL;
@@ -49,7 +49,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
protected override AssetBase GetAsset(UUID uuid)
{
if (string.Empty == m_assetServerURL)
return m_assetCache.Get(uuid.ToString());
return base.GetAsset(uuid);
else
return m_assetMapper.FetchAsset(m_assetServerURL, uuid);
}

View File

@@ -95,14 +95,14 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring
{
foreach (IMonitor monitor in m_staticMonitors)
{
m_log.InfoFormat(
MainConsole.Instance.OutputFormat(
"[MONITOR MODULE]: {0} reports {1} = {2}",
m_scene.RegionInfo.RegionName, monitor.GetFriendlyName(), monitor.GetFriendlyValue());
}
foreach (KeyValuePair<string, float> tuple in m_scene.StatsReporter.GetExtraSimStats())
{
m_log.InfoFormat(
MainConsole.Instance.OutputFormat(
"[MONITOR MODULE]: {0} reports {1} = {2}",
m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value);
}

View File

@@ -31,6 +31,7 @@ using System.Reflection;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.ClientStack.LindenUDP;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Drawing;
using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class DynamicTexture : IDynamicTexture
{
public string InputCommands { get; private set; }
public Uri InputUri { get; private set; }
public string InputParams { get; private set; }
public byte[] Data { get; private set; }
public Size Size { get; private set; }
public bool IsReuseable { get; private set; }
public DynamicTexture(string inputCommands, string inputParams, byte[] data, Size size, bool isReuseable)
{
InputCommands = inputCommands;
InputParams = inputParams;
Data = data;
Size = size;
IsReuseable = isReuseable;
}
public DynamicTexture(Uri inputUri, string inputParams, byte[] data, Size size, bool isReuseable)
{
InputUri = inputUri;
InputParams = inputParams;
Data = data;
Size = size;
IsReuseable = isReuseable;
}
}
}

View File

@@ -42,13 +42,29 @@ 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;
public const int DISP_EXPIRE = 1;
public const int DISP_TEMP = 2;
/// <summary>
/// If true then where possible dynamic textures are reused.
/// </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 =
@@ -56,6 +72,15 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
private Dictionary<UUID, DynamicTextureUpdater> Updaters = new Dictionary<UUID, DynamicTextureUpdater>();
/// <summary>
/// Record dynamic textures that we can reuse for a given data and parameter combination rather than
/// regenerate.
/// </summary>
/// <remarks>
/// Key is string.Format("{0}{1}", data
/// </remarks>
private Cache m_reuseableDynamicTextures;
#region IDynamicTextureManager Members
public void RegisterRender(string handleType, IDynamicTextureRender render)
@@ -69,17 +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>
public void ReturnData(UUID id, byte[] data)
/// <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];
}
}
@@ -88,7 +113,16 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
if (RegisteredScenes.ContainsKey(updater.SimUUID))
{
Scene scene = RegisteredScenes[updater.SimUUID];
updater.DataReceived(data, scene);
UUID newTextureID = updater.DataReceived(texture.Data, scene);
if (ReuseTextures
&& !updater.BlendWithOldTexture
&& texture.IsReuseable
&& (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
{
m_reuseableDynamicTextures.Store(
GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
}
}
}
@@ -104,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)
{
@@ -167,22 +222,61 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
string extraParams, int updateTimer, bool SetBlending, int disp, byte AlphaValue, int face)
{
if (RenderPlugins.ContainsKey(contentType))
{
DynamicTextureUpdater updater = new DynamicTextureUpdater();
updater.SimUUID = simID;
updater.PrimID = primID;
updater.ContentType = contentType;
updater.BodyData = data;
updater.UpdateTimer = updateTimer;
updater.UpdaterID = UUID.Random();
updater.Params = extraParams;
updater.BlendWithOldTexture = SetBlending;
updater.FrontAlpha = AlphaValue;
updater.Face = face;
updater.Url = "Local image";
updater.Disp = disp;
if (!RenderPlugins.ContainsKey(contentType))
return UUID.Zero;
Scene scene;
RegisteredScenes.TryGetValue(simID, out scene);
if (scene == null)
return UUID.Zero;
SceneObjectPart part = scene.GetSceneObjectPart(primID);
if (part == null)
return UUID.Zero;
// If we want to reuse dynamic textures then we have to ignore any request from the caller to expire
// them.
if (ReuseTextures)
disp = disp & ~DISP_EXPIRE;
DynamicTextureUpdater updater = new DynamicTextureUpdater();
updater.SimUUID = simID;
updater.PrimID = primID;
updater.ContentType = contentType;
updater.BodyData = data;
updater.UpdateTimer = updateTimer;
updater.UpdaterID = UUID.Random();
updater.Params = extraParams;
updater.BlendWithOldTexture = SetBlending;
updater.FrontAlpha = AlphaValue;
updater.Face = face;
updater.Url = "Local image";
updater.Disp = disp;
object objReusableTextureUUID = null;
if (ReuseTextures && !updater.BlendWithOldTexture)
{
string reuseableTextureKey = GenerateReusableTextureKey(data, extraParams);
objReusableTextureUUID = m_reuseableDynamicTextures.Get(reuseableTextureKey);
if (objReusableTextureUUID != null)
{
// If something else has removed this temporary asset from the cache, detect and invalidate
// our cached uuid.
if (scene.AssetService.GetMetadata(objReusableTextureUUID.ToString()) == null)
{
m_reuseableDynamicTextures.Invalidate(reuseableTextureKey);
objReusableTextureUUID = null;
}
}
}
// We cannot reuse a dynamic texture if the data is going to be blended with something already there.
if (objReusableTextureUUID == null)
{
lock (Updaters)
{
if (!Updaters.ContainsKey(updater.UpdaterID))
@@ -191,11 +285,29 @@ 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);
return updater.UpdaterID;
}
return UUID.Zero;
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);
}
return updater.UpdaterID;
}
private string GenerateReusableTextureKey(string data, string extraParams)
{
return string.Format("{0}{1}", data, extraParams);
}
public void GetDrawStringSize(string contentType, string text, string fontName, int fontSize,
@@ -215,6 +327,13 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public void Initialise(Scene scene, IConfigSource config)
{
IConfig texturesConfig = config.Configs["Textures"];
if (texturesConfig != null)
{
ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
}
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
{
RegisteredScenes.Add(scene.RegionInfo.RegionID, scene);
@@ -224,6 +343,11 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public void PostInitialise()
{
if (ReuseTextures)
{
m_reuseableDynamicTextures = new Cache(CacheMedium.Memory, CacheStrategy.Conservative);
m_reuseableDynamicTextures.DefaultTTL = new TimeSpan(24, 0, 0);
}
}
public void Close()
@@ -268,10 +392,61 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
BodyData = null;
}
/// <summary>
/// Update the given part with the new texture.
/// </summary>
/// <returns>
/// The old texture UUID.
/// </returns>
public UUID UpdatePart(SceneObjectPart part, UUID textureID)
{
UUID oldID;
lock (part)
{
// mostly keep the values from before
Primitive.TextureEntry tmptex = part.Shape.Textures;
// FIXME: Need to return the appropriate ID if only a single face is replaced.
oldID = tmptex.DefaultTexture.TextureID;
if (Face == ALL_SIDES)
{
oldID = tmptex.DefaultTexture.TextureID;
tmptex.DefaultTexture.TextureID = textureID;
}
else
{
try
{
Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
texface.TextureID = textureID;
tmptex.FaceTextures[Face] = texface;
}
catch (Exception)
{
tmptex.DefaultTexture.TextureID = textureID;
}
}
// I'm pretty sure we always want to force this to true
// I'm pretty sure noone whats to set fullbright true if it wasn't true before.
// tmptex.DefaultTexture.Fullbright = true;
part.UpdateTextureEntry(tmptex.GetBytes());
}
return oldID;
}
/// <summary>
/// Called once new texture data has been received for this updater.
/// </summary>
public void DataReceived(byte[] data, Scene scene)
/// <param name="data"></param>
/// <param name="scene"></param>
/// <param name="isReuseable">True if the data given is reuseable.</param>
/// <returns>The asset UUID given to the incoming data.</returns>
public UUID DataReceived(byte[] data, Scene scene)
{
SceneObjectPart part = scene.GetSceneObjectPart(PrimID);
@@ -281,7 +456,8 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
String.Format("DynamicTextureModule: Error preparing image using URL {0}", Url);
scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,
0, part.ParentGroup.RootPart.AbsolutePosition, part.Name, part.UUID, false);
return;
return UUID.Zero;
}
byte[] assetData = null;
@@ -319,56 +495,29 @@ 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 = UUID.Zero;
lock (part)
{
// mostly keep the values from before
Primitive.TextureEntry tmptex = part.Shape.Textures;
// remove the old asset from the cache
oldID = tmptex.DefaultTexture.TextureID;
if (Face == ALL_SIDES)
{
tmptex.DefaultTexture.TextureID = asset.FullID;
}
else
{
try
{
Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
texface.TextureID = asset.FullID;
tmptex.FaceTextures[Face] = texface;
}
catch (Exception)
{
tmptex.DefaultTexture.TextureID = asset.FullID;
}
}
// I'm pretty sure we always want to force this to true
// I'm pretty sure noone whats to set fullbright true if it wasn't true before.
// tmptex.DefaultTexture.Fullbright = true;
part.UpdateTextureEntry(tmptex.GetBytes());
}
UUID oldID = UpdatePart(part, asset.FullID);
if (oldID != UUID.Zero && ((Disp & DISP_EXPIRE) != 0))
{
if (oldAsset == null) oldAsset = scene.AssetService.Get(oldID.ToString());
if (oldAsset == null)
oldAsset = scene.AssetService.Get(oldID.ToString());
if (oldAsset != null)
{
if (oldAsset.Temporary == true)
if (oldAsset.Temporary)
{
scene.AssetService.Delete(oldID.ToString());
}
}
}
return asset.FullID;
}
private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha)

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;
@@ -67,12 +68,18 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
return true;
}
public byte[] ConvertUrl(string url, string extraParams)
// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
// {
// // We don't support conversion of body data.
// return false;
// }
public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
public byte[] ConvertStream(Stream data, string extraParams)
public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
return null;
}
@@ -165,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
{
@@ -182,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)
{
@@ -227,7 +240,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
catch (WebException)
{
}
finally
{
@@ -236,9 +248,14 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
stream.Close();
}
}
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);
m_textureManager.ReturnData(
state.RequestID,
new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
request.RequestUri, null, imageJ2000, newSize, false));
}
#region Nested type: RequestState

View File

@@ -45,31 +45,292 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
{
[TestFixture]
public class VectorRenderModuleTests
public class VectorRenderModuleTests : OpenSimTestCase
{
Scene m_scene;
DynamicTextureModule m_dtm;
VectorRenderModule m_vrm;
private void SetupScene(bool reuseTextures)
{
m_scene = new SceneHelpers().SetupScene();
m_dtm = new DynamicTextureModule();
m_dtm.ReuseTextures = reuseTextures;
// m_dtm.ReuseLowDataTextures = reuseTextures;
m_vrm = new VectorRenderModule();
SceneHelpers.SetupSceneModules(m_scene, m_dtm, m_vrm);
}
[Test]
public void TestDraw()
{
TestHelpers.InMethod();
Scene scene = new SceneHelpers().SetupScene();
DynamicTextureModule dtm = new DynamicTextureModule();
VectorRenderModule vrm = new VectorRenderModule();
SceneHelpers.SetupSceneModules(scene, dtm, vrm);
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene);
SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
dtm.AddDynamicTextureData(
scene.RegionInfo.RegionID,
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
vrm.GetContentType(),
m_vrm.GetContentType(),
"PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;",
"",
0);
Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDraw()
{
TestHelpers.InMethod();
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDrawDifferentExtraParams()
{
TestHelpers.InMethod();
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"alpha:250",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDrawContainingImage()
{
TestHelpers.InMethod();
string dtText
= "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestDrawReusingTexture()
{
TestHelpers.InMethod();
SetupScene(true);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
"PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;",
"",
0);
Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDrawReusingTexture()
{
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,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
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()
{
TestHelpers.InMethod();
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,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"alpha:250",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDrawContainingImageReusingTexture()
{
TestHelpers.InMethod();
string dtText
= "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
SetupScene(true);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
}
}

View File

@@ -30,10 +30,12 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
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;
@@ -45,9 +47,13 @@ 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 string m_name = "VectorRenderModule";
private Scene m_scene;
private IDynamicTextureManager m_textureManager;
private Graphics m_graph;
@@ -61,12 +67,12 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public string GetContentType()
{
return ("vector");
return "vector";
}
public string GetName()
{
return m_name;
return Name;
}
public bool SupportsAsynchronous()
@@ -74,14 +80,20 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
return true;
}
public byte[] ConvertUrl(string url, string extraParams)
// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
// {
// string[] lines = GetLines(bodyData);
// return lines.Any((str, r) => str.StartsWith("Image"));
// }
public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
public byte[] ConvertStream(Stream data, string extraParams)
public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
return null;
return Draw(bodyData, extraParams);
}
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
@@ -91,21 +103,28 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
{
Draw(bodyData, id, extraParams);
// XXX: This isn't actually being done asynchronously!
m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
return true;
}
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;
}
}
}
}
@@ -144,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()
@@ -152,7 +178,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public string Name
{
get { return m_name; }
get { return "VectorRenderModule"; }
}
public bool IsSharedModule
@@ -162,7 +188,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
#endregion
private void Draw(string data, UUID id, string extraParams)
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
@@ -305,40 +331,57 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
Bitmap bitmap = null;
Graphics graph = null;
bool reuseable = false;
try
{
if (alpha == 256)
bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
else
bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
graph = Graphics.FromImage(bitmap);
// this is really just to save people filling the
// background color in their scripts, only do when fully opaque
if (alpha >= 255)
// XXX: In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
// the native malloc heap can become corrupted, possibly due to a double free(). This may be due to
// bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX. These problems were
// seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1. They go away if disposal is perfomed
// under lock.
lock (this)
{
using (SolidBrush bgFillBrush = new SolidBrush(bgColor))
{
graph.FillRectangle(bgFillBrush, 0, 0, width, height);
}
}
if (alpha == 256)
bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
else
bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
for (int w = 0; w < bitmap.Width; w++)
{
if (alpha <= 255)
graph = Graphics.FromImage(bitmap);
// this is really just to save people filling the
// background color in their scripts, only do when fully opaque
if (alpha >= 255)
{
for (int h = 0; h < bitmap.Height; h++)
using (SolidBrush bgFillBrush = new SolidBrush(bgColor))
{
bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
graph.FillRectangle(bgFillBrush, 0, 0, width, height);
}
}
for (int w = 0; w < bitmap.Width; w++)
{
if (alpha <= 255)
{
for (int h = 0; h < bitmap.Height; h++)
{
bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
}
}
}
GDIDraw(data, graph, altDataDelim, out reuseable);
}
GDIDraw(data, graph, altDataDelim);
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
{
@@ -351,15 +394,24 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
e.Message, e.StackTrace);
}
m_textureManager.ReturnData(id, imageJ2000);
return new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
data, extraParams, imageJ2000, new Size(width, height), reuseable);
}
finally
{
if (graph != null)
graph.Dispose();
if (bitmap != null)
bitmap.Dispose();
// XXX: In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
// the native malloc heap can become corrupted, possibly due to a double free(). This may be due to
// bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX. These problems were
// seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1. They go away if disposal is perfomed
// under lock.
lock (this)
{
if (graph != null)
graph.Dispose();
if (bitmap != null)
bitmap.Dispose();
}
}
}
@@ -418,8 +470,21 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
*/
private void GDIDraw(string data, Graphics graph, char dataDelim)
/// <summary>
/// Split input data into discrete command lines.
/// </summary>
/// <returns></returns>
/// <param name='data'></param>
/// <param name='dataDelim'></param>
private string[] GetLines(string data, char dataDelim)
{
char[] lineDelimiter = { dataDelim };
return data.Split(lineDelimiter);
}
private void GDIDraw(string data, Graphics graph, char dataDelim, out bool reuseable)
{
reuseable = true;
Point startPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
Pen drawPen = null;
@@ -434,11 +499,9 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
myFont = new Font(fontName, fontSize);
myBrush = new SolidBrush(Color.Black);
char[] lineDelimiter = {dataDelim};
char[] partsDelimiter = {','};
string[] lines = data.Split(lineDelimiter);
foreach (string line in lines)
foreach (string line in GetLines(data, dataDelim))
{
string nextLine = line.Trim();
//replace with switch, or even better, do some proper parsing
@@ -469,6 +532,10 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
else if (nextLine.StartsWith("Image"))
{
// We cannot reuse any generated texture involving fetching an image via HTTP since that image
// can change.
reuseable = false;
float x = 0;
float y = 0;
GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);

View File

@@ -204,8 +204,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
public byte[] GetData(string id)
{
// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
AssetBase asset = m_Cache.Get(id);
AssetBase asset = null;
if (m_Cache != null)
asset = m_Cache.Get(id);
if (asset != null)
return asset.Data;

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using log4net.Config;
using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset;
using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset.Tests
{
[TestFixture]
public class AssetConnectorsTests : OpenSimTestCase
{
[Test]
public void TestAddAsset()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
config.AddConfig("AssetService");
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
lasc.Initialise(config);
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
lasc.Store(a1);
AssetBase retreivedA1 = lasc.Get(a1.ID);
Assert.That(retreivedA1.ID, Is.EqualTo(a1.ID));
Assert.That(retreivedA1.Metadata.ID, Is.EqualTo(a1.Metadata.ID));
Assert.That(retreivedA1.Data.Length, Is.EqualTo(a1.Data.Length));
AssetMetadata retrievedA1Metadata = lasc.GetMetadata(a1.ID);
Assert.That(retrievedA1Metadata.ID, Is.EqualTo(a1.ID));
byte[] retrievedA1Data = lasc.GetData(a1.ID);
Assert.That(retrievedA1Data.Length, Is.EqualTo(a1.Data.Length));
// TODO: Add cache and check that this does receive a copy of the asset
}
[Test]
public void TestAddTemporaryAsset()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
config.AddConfig("AssetService");
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
lasc.Initialise(config);
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
a1.Temporary = true;
lasc.Store(a1);
Assert.That(lasc.Get(a1.ID), Is.Null);
Assert.That(lasc.GetData(a1.ID), Is.Null);
Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
// TODO: Add cache and check that this does receive a copy of the asset
}
[Test]
public void TestAddLocalAsset()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
config.AddConfig("AssetService");
config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
lasc.Initialise(config);
AssetBase a1 = AssetHelpers.CreateNotecardAsset();
a1.Local = true;
lasc.Store(a1);
Assert.That(lasc.Get(a1.ID), Is.Null);
Assert.That(lasc.GetData(a1.ID), Is.Null);
Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
// TODO: Add cache and check that this does receive a copy of the asset
}
}
}

View File

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

View File

@@ -312,7 +312,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
// "[LOCAL SIMULATION CONNECTOR]: Found region {0} {1} to send AgentUpdate",
// s.RegionInfo.RegionName, destination.RegionHandle);
Util.FireAndForget(delegate { m_scenes[destination.RegionID].IncomingCloseAgent(id); });
Util.FireAndForget(delegate { m_scenes[destination.RegionID].IncomingCloseAgent(id, false); });
return true;
}

View File

@@ -51,7 +51,7 @@ using RegionSettings = OpenSim.Framework.RegionSettings;
namespace OpenSim.Region.CoreModules.World.Archiver.Tests
{
[TestFixture]
public class ArchiverTests
public class ArchiverTests : OpenSimTestCase
{
private Guid m_lastRequestId;
private string m_lastErrorMessage;

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

@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@@ -83,29 +84,56 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.Commands.AddCommand(
"Objects", false, "delete object owner",
"delete object owner <UUID>",
"Delete a scene object by owner", HandleDeleteObject);
"Delete scene objects by owner",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object creator",
"delete object creator <UUID>",
"Delete a scene object by creator", HandleDeleteObject);
"Delete scene objects by creator",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object uuid",
"delete object uuid <UUID>",
"Delete a scene object by uuid", HandleDeleteObject);
"Delete a scene object by uuid",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object name",
"delete object name [--regex] <name>",
"Delete a scene object by name.",
"If --regex is specified then the name is treatead as a regular expression",
"Command will ask for confirmation before proceeding.\n"
+ "If --regex is specified then the name is treatead as a regular expression",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object outside",
"delete object outside",
"Delete all scene objects outside region boundaries", HandleDeleteObject);
"Delete all scene objects outside region boundaries",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects",
false,
"delete object pos",
"delete object pos <start-coord> to <end-coord>",
"Delete scene objects within the given area.",
"Each component of the coord is comma separated. There must be no spaces between the commas.\n"
+ "If you don't care about the z component you can simply omit it.\n"
+ "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
+ "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
+ "If you want to specify the minimum value of a component then you can use -~ instead of a number\n"
+ "e.g.\n"
+ "delete object pos 20,20,20 to 40,40,40\n"
+ "delete object pos 20,20 to 40,40\n"
+ "delete object pos ,20,20 to ,40,40\n"
+ "delete object pos ,,30 to ,,~\n"
+ "delete object pos ,,-~ to ,,30",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects",
@@ -123,6 +151,25 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
"If --regex is specified then the name is treatead as a regular expression",
HandleShowObjectByName);
m_console.Commands.AddCommand(
"Objects",
false,
"show object pos",
"show object pos <start-coord> to <end-coord>",
"Show details of scene objects within the given area.",
"Each component of the coord is comma separated. There must be no spaces between the commas.\n"
+ "If you don't care about the z component you can simply omit it.\n"
+ "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
+ "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
+ "If you want to specify the minimum value of a component then you can use -~ instead of a number\n"
+ "e.g.\n"
+ "show object pos 20,20,20 to 40,40,40\n"
+ "show object pos 20,20 to 40,40\n"
+ "show object pos ,20,20 to ,40,40\n"
+ "show object pos ,,30 to ,,~\n"
+ "show object pos ,,-~ to ,,30",
HandleShowObjectByPos);
m_console.Commands.AddCommand(
"Objects",
false,
@@ -138,6 +185,25 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
"Show details of scene object parts with the given name.",
"If --regex is specified then the name is treatead as a regular expression",
HandleShowPartByName);
m_console.Commands.AddCommand(
"Objects",
false,
"show part pos",
"show part pos <start-coord> to <end-coord>",
"Show details of scene object parts within the given area.",
"Each component of the coord is comma separated. There must be no spaces between the commas.\n"
+ "If you don't care about the z component you can simply omit it.\n"
+ "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
+ "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
+ "If you want to specify the minimum value of a component then you can use -~ instead of a number\n"
+ "e.g.\n"
+ "show object pos 20,20,20 to 40,40,40\n"
+ "show object pos 20,20 to 40,40\n"
+ "show object pos ,20,20 to ,40,40\n"
+ "show object pos ,,30 to ,,~\n"
+ "show object pos ,,-~ to ,,30",
HandleShowPartByPos);
}
public void RemoveRegion(Scene scene)
@@ -150,6 +216,43 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
// m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
}
private void OutputSogsToConsole(Predicate<SceneObjectGroup> searchPredicate)
{
List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate);
StringBuilder sb = new StringBuilder();
foreach (SceneObjectGroup so in sceneObjects)
{
AddSceneObjectReport(sb, so);
sb.Append("\n");
}
sb.AppendFormat("{0} object(s) found in {1}\n", sceneObjects.Count, m_scene.Name);
m_console.OutputFormat(sb.ToString());
}
private void OutputSopsToConsole(Predicate<SceneObjectPart> searchPredicate)
{
List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups();
List<SceneObjectPart> parts = new List<SceneObjectPart>();
sceneObjects.ForEach(so => parts.AddRange(Array.FindAll<SceneObjectPart>(so.Parts, searchPredicate)));
StringBuilder sb = new StringBuilder();
foreach (SceneObjectPart part in parts)
{
AddScenePartReport(sb, part);
sb.Append("\n");
}
sb.AppendFormat("{0} parts found in {1}\n", parts.Count, m_scene.Name);
m_console.OutputFormat(sb.ToString());
}
private void HandleShowObjectByUuid(string module, string[] cmd)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
@@ -200,36 +303,41 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
string name = mainParams[3];
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
Action<SceneObjectGroup> searchAction;
Predicate<SceneObjectGroup> searchPredicate;
if (useRegex)
{
Regex nameRegex = new Regex(name);
searchAction = so => { if (nameRegex.IsMatch(so.Name)) { sceneObjects.Add(so); }};
searchPredicate = so => nameRegex.IsMatch(so.Name);
}
else
{
searchAction = so => { if (so.Name == name) { sceneObjects.Add(so); }};
searchPredicate = so => so.Name == name;
}
m_scene.ForEachSOG(searchAction);
OutputSogsToConsole(searchPredicate);
}
if (sceneObjects.Count == 0)
private void HandleShowObjectByPos(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
if (cmdparams.Length < 5)
{
m_console.OutputFormat("No objects with name {0} found in {1}", name, m_scene.RegionInfo.RegionName);
m_console.OutputFormat("Usage: show object pos <start-coord> to <end-coord>");
return;
}
StringBuilder sb = new StringBuilder();
Vector3 startVector, endVector;
foreach (SceneObjectGroup so in sceneObjects)
{
AddSceneObjectReport(sb, so);
sb.Append("\n");
}
if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
return;
m_console.OutputFormat(sb.ToString());
Predicate<SceneObjectGroup> searchPredicate
= so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector);
OutputSogsToConsole(searchPredicate);
}
private void HandleShowPartByUuid(string module, string[] cmd)
@@ -264,6 +372,38 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.OutputFormat(sb.ToString());
}
private void HandleShowPartByPos(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
if (cmdparams.Length < 5)
{
m_console.OutputFormat("Usage: show part pos <start-coord> to <end-coord>");
return;
}
string rawConsoleStartVector = cmdparams[3];
Vector3 startVector;
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
{
m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
return;
}
string rawConsoleEndVector = cmdparams[5];
Vector3 endVector;
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
{
m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
return;
}
OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector));
}
private void HandleShowPartByName(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
@@ -282,37 +422,19 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
string name = mainParams[3];
List<SceneObjectPart> parts = new List<SceneObjectPart>();
Action<SceneObjectGroup> searchAction;
Predicate<SceneObjectPart> searchPredicate;
if (useRegex)
{
Regex nameRegex = new Regex(name);
searchAction = so => so.ForEachPart(sop => { if (nameRegex.IsMatch(sop.Name)) { parts.Add(sop); } });
searchPredicate = sop => nameRegex.IsMatch(sop.Name);
}
else
{
searchAction = so => so.ForEachPart(sop => { if (sop.Name == name) { parts.Add(sop); } });
searchPredicate = sop => sop.Name == name;
}
m_scene.ForEachSOG(searchAction);
if (parts.Count == 0)
{
m_console.OutputFormat("No parts with name {0} found in {1}", name, m_scene.RegionInfo.RegionName);
return;
}
StringBuilder sb = new StringBuilder();
foreach (SceneObjectPart part in parts)
{
AddScenePartReport(sb, part);
sb.Append("\n");
}
m_console.OutputFormat(sb.ToString());
OutputSopsToConsole(searchPredicate);
}
private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so)
@@ -450,6 +572,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
break;
case "pos":
deletes = GetDeleteCandidatesByPos(module, cmd);
break;
default:
m_console.OutputFormat("Unrecognized mode {0}", mode);
return;
@@ -464,7 +590,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
string.Format(
"Are you sure that you want to delete {0} objects from {1}",
deletes.Count, m_scene.RegionInfo.RegionName),
"n");
"y/N");
if (response.ToLower() != "y")
{
@@ -486,9 +612,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return null;
bool useRegex = false;
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
@@ -522,5 +645,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
return sceneObjects;
}
/// <summary>
/// Get scene object delete candidates by position
/// </summary>
/// <param name='module'></param>
/// <param name='cmdparams'></param>
/// <returns>null if parsing failed on one of the arguments, otherwise a list of objects to delete. If there
/// are no objects to delete then the list will be empty./returns>
private List<SceneObjectGroup> GetDeleteCandidatesByPos(string module, string[] cmdparams)
{
if (cmdparams.Length < 5)
{
m_console.OutputFormat("Usage: delete object pos <start-coord> to <end-coord>");
return null;
}
Vector3 startVector, endVector;
if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
return null;
return m_scene.GetSceneObjectGroups().FindAll(
so => !so.IsAttachment && Util.IsInsideBox(so.AbsolutePosition, startVector, endVector));
}
private bool TryParseVectorRange(IEnumerable<string> rawComponents, out Vector3 startVector, out Vector3 endVector)
{
string rawConsoleStartVector = rawComponents.Take(1).Single();
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
{
m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
endVector = Vector3.Zero;
return false;
}
string rawConsoleEndVector = rawComponents.Skip(1).Take(1).Single();
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
{
m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
return false;
}
return true;
}
}
}

View File

@@ -85,13 +85,15 @@ namespace OpenSim.Region.CoreModules.World.Sound
dis = 0;
}
float thisSpGain;
// Scale by distance
if (radius == 0)
gain = (float)((double)gain * ((100.0 - dis) / 100.0));
thisSpGain = (float)((double)gain * ((100.0 - dis) / 100.0));
else
gain = (float)((double)gain * ((radius - dis) / radius));
thisSpGain = (float)((double)gain * ((radius - dis) / radius));
sp.ControllingClient.SendPlayAttachedSound(soundID, objectID, ownerID, (float)gain, flags);
sp.ControllingClient.SendPlayAttachedSound(soundID, objectID, ownerID, thisSpGain, flags);
});
}

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);
/// <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);
@@ -113,11 +122,65 @@ namespace OpenSim.Region.Framework.Interfaces
string GetName();
string GetContentType();
bool SupportsAsynchronous();
byte[] ConvertUrl(string url, string extraParams);
byte[] ConvertStream(Stream data, string extraParams);
// /// <summary>
// /// Return true if converting the input body and extra params data will always result in the same byte[] array
// /// </summary>
// /// <remarks>
// /// This method allows the caller to use a previously generated asset if it has one.
// /// </remarks>
// /// <returns></returns>
// /// <param name='bodyData'></param>
// /// <param name='extraParams'></param>
// bool AlwaysIdenticalConversion(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

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

@@ -46,9 +46,31 @@ namespace OpenSim.Region.Framework.Interfaces
/// </summary>
event ScriptCommand OnScriptCommand;
/// <summary>
/// Register an instance method as a script call by method name
/// </summary>
/// <param name="target"></param>
/// <param name="method"></param>
void RegisterScriptInvocation(object target, string method);
/// <summary>
/// Register an instance method as a script call by method info
/// </summary>
/// <param name="target"></param>
/// <param name="method"></param>
void RegisterScriptInvocation(object target, MethodInfo method);
/// <summary>
/// Register one or more instance methods as script calls by method name
/// </summary>
/// <param name="target"></param>
/// <param name="methods"></param>
void RegisterScriptInvocation(object target, string[] methods);
/// <summary>
/// Returns an array of all registered script calls
/// </summary>
/// <returns></returns>
Delegate[] GetScriptInvocationList();
Delegate LookupScriptInvocation(string fname);

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);
@@ -76,33 +121,96 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
/// <remarks>
/// This is triggered for both child and root agent client connections.
///
/// Triggered before OnClientLogin.
///
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks>
public event OnNewClientDelegate OnNewClient;
/// <summary>
/// Fired if the client entering this sim is doing so as a new login
/// </summary>
/// <remarks>
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks>
public event Action<IClientAPI> OnClientLogin;
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>
///
/// Triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </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 +225,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 +261,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 +309,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);
@@ -200,6 +436,9 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
/// <remarks>
/// At the point of firing, the scene still contains the client's scene presence.
///
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks>
public event ClientClosed OnClientClosed;
@@ -210,6 +449,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;
@@ -241,6 +483,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;
@@ -266,48 +514,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;
@@ -333,9 +736,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);
@@ -345,12 +756,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();
@@ -366,6 +792,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;
@@ -478,6 +918,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);
@@ -519,9 +962,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
@@ -529,7 +991,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;
@@ -586,8 +1050,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)
@@ -2003,7 +2488,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");

View File

@@ -93,7 +93,7 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public void StartScripts()
{
m_log.InfoFormat("[SCENE]: Starting scripts in {0}, please wait.", RegionInfo.RegionName);
// m_log.InfoFormat("[SCENE]: Starting scripts in {0}, please wait.", RegionInfo.RegionName);
IScriptModule[] engines = RequestModuleInterfaces<IScriptModule>();

View File

@@ -468,7 +468,7 @@ namespace OpenSim.Region.Framework.Scenes
if (!InventoryService.AddFolder(folder))
{
m_log.WarnFormat(
"[AGENT INVENTORY]: Failed to move create folder for user {0} {1}",
"[AGENT INVENTORY]: Failed to create folder for user {0} {1}",
remoteClient.Name, remoteClient.AgentId);
}
}

View File

@@ -79,6 +79,11 @@ namespace OpenSim.Region.Framework.Scenes
public SynchronizeSceneHandler SynchronizeScene;
/// <summary>
/// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other.
/// </summary>
private object m_removeClientLock = new object();
/// <summary>
/// Statistical information for this scene.
/// </summary>
@@ -128,7 +133,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;
@@ -275,6 +281,31 @@ namespace OpenSim.Region.Framework.Scenes
}
private volatile bool m_shuttingDown;
/// <summary>
/// Is the scene active?
/// </summary>
/// <remarks>
/// If false, maintenance and update loops are not being run. Updates can still be triggered manually if
/// the scene is not active.
/// </remarks>
public bool Active
{
get { return m_active; }
set
{
if (value)
{
if (!m_active)
Start();
}
else
{
m_active = false;
}
}
}
private volatile bool m_active;
// private int m_lastUpdate;
// private bool m_firstHeartbeat = true;
@@ -714,6 +745,8 @@ 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);
@@ -754,13 +787,6 @@ namespace OpenSim.Region.Framework.Scenes
SpawnPointRouting = startupConfig.GetString("SpawnPointRouting", "closest");
TelehubAllowLandmarks = startupConfig.GetBoolean("TelehubAllowLandmark", false);
IConfig packetConfig = m_config.Configs["PacketPool"];
if (packetConfig != null)
{
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
}
m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl);
m_generateMaptiles = startupConfig.GetBoolean("GenerateMaptiles", true);
@@ -817,6 +843,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)
{
@@ -1112,6 +1140,14 @@ namespace OpenSim.Region.Framework.Scenes
public void SetSceneCoreDebug(Dictionary<string, string> options)
{
if (options.ContainsKey("active"))
{
bool active;
if (bool.TryParse(options["active"], out active))
Active = active;
}
if (options.ContainsKey("scripting"))
{
bool enableScripts = true;
@@ -1251,6 +1287,8 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public void Start()
{
m_active = true;
// m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName);
//m_heartbeatTimer.Enabled = true;
@@ -1292,7 +1330,7 @@ namespace OpenSim.Region.Framework.Scenes
#region Update Methods
/// <summary>
/// Performs per-frame updates regularly
/// Activate the various loops necessary to continually update the scene.
/// </summary>
private void Heartbeat()
{
@@ -1349,7 +1387,7 @@ namespace OpenSim.Region.Framework.Scenes
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun))
while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun))
{
runtc = Util.EnvironmentTickCount();
++MaintenanceRun;
@@ -1408,7 +1446,7 @@ namespace OpenSim.Region.Framework.Scenes
int previousFrameTick, tmpMS;
int maintc = Util.EnvironmentTickCount();
while (!m_shuttingDown && (endFrame == null || Frame < endFrame))
while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame))
{
++Frame;
@@ -2660,69 +2698,89 @@ namespace OpenSim.Region.Framework.Scenes
public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)
{
ScenePresence sp;
bool vialogin;
// Validation occurs in LLUDPServer
//
// XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with
// each other. In practice, this does not currently occur in the code.
AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode);
bool vialogin
= (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
|| (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
// CheckHeartbeat();
ScenePresence sp = GetScenePresence(client.AgentId);
// XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
// could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause
// other problems, and possible the code calling AddNewClient() should ensure that no client is already
// connected.
if (sp == null)
// We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection
// and a simultaneous one that removes it (as can happen if the client is closed at a particular point
// whilst connecting).
//
// It would be easier to lock across all NewUserConnection(), AddNewClient() and
// RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service
// response in some module listening to AddNewClient()) from holding up unrelated agent calls.
//
// In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all
// AddNewClient() operations (though not other ops).
// In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved.
lock (aCircuit)
{
m_log.DebugFormat(
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
vialogin
= (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
|| (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
// CheckHeartbeat();
sp = GetScenePresence(client.AgentId);
m_clientManager.Add(client);
SubscribeToClientEvents(client);
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
m_eventManager.TriggerOnNewPresence(sp);
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
// The first agent upon login is a root agent by design.
// For this agent we will have to rez the attachments.
// All other AddNewClient calls find aCircuit.child to be true.
if (aCircuit.child == false)
// XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
// could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause
// other problems, and possible the code calling AddNewClient() should ensure that no client is already
// connected.
if (sp == null)
{
// We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to
// start the scripts again (since this is done in RezAttachments()).
// XXX: This is convoluted.
sp.IsChildAgent = false;
if (AttachmentsModule != null)
Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
m_log.DebugFormat(
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
m_clientManager.Add(client);
SubscribeToClientEvents(client);
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
m_eventManager.TriggerOnNewPresence(sp);
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
// The first agent upon login is a root agent by design.
// For this agent we will have to rez the attachments.
// All other AddNewClient calls find aCircuit.child to be true.
if (aCircuit.child == false)
{
// We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to
// start the scripts again (since this is done in RezAttachments()).
// XXX: This is convoluted.
sp.IsChildAgent = false;
if (AttachmentsModule != null)
Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
}
}
}
else
{
m_log.WarnFormat(
"[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
}
else
{
m_log.WarnFormat(
"[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
}
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
client.SceneAgent = sp;
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
client.SceneAgent = sp;
// Cache the user's name
CacheUserName(sp, aCircuit);
EventManager.TriggerOnNewClient(client);
if (vialogin)
EventManager.TriggerOnClientLogin(client);
}
m_LastLogin = Util.EnvironmentTickCount();
// Cache the user's name
CacheUserName(sp, aCircuit);
EventManager.TriggerOnNewClient(client);
if (vialogin)
EventManager.TriggerOnClientLogin(client);
return sp;
}
@@ -3251,109 +3309,129 @@ namespace OpenSim.Region.Framework.Scenes
{
// CheckHeartbeat();
bool isChildAgent = false;
ScenePresence avatar = GetScenePresence(agentID);
AgentCircuitData acd;
if (avatar == null)
lock (m_removeClientLock)
{
m_log.WarnFormat(
"[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
acd = m_authenticateHandler.GetAgentCircuitData(agentID);
return;
if (acd == null)
{
m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID);
return;
}
else
{
// We remove the acd up here to avoid later raec conditions if two RemoveClient() calls occurred
// simultaneously.
m_authenticateHandler.RemoveCircuit(acd.circuitcode);
}
}
try
lock (acd)
{
isChildAgent = avatar.IsChildAgent;
m_log.DebugFormat(
"[SCENE]: Removing {0} agent {1} {2} from {3}",
(isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
// Don't do this to root agents, it's not nice for the viewer
if (closeChildAgents && isChildAgent)
ScenePresence avatar = GetScenePresence(agentID);
if (avatar == null)
{
// Tell a single agent to disconnect from the region.
IEventQueue eq = RequestModuleInterface<IEventQueue>();
if (eq != null)
{
eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
}
else
{
avatar.ControllingClient.SendShutdownConnectionNotice();
}
m_log.WarnFormat(
"[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
return;
}
// Only applies to root agents.
if (avatar.ParentID != 0)
{
avatar.StandUp();
}
m_sceneGraph.removeUserCount(!isChildAgent);
// TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
// unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
if (closeChildAgents && CapsModule != null)
CapsModule.RemoveCaps(agentID);
// REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
// this method is doing is HORRIBLE!!!
avatar.Scene.NeedSceneCacheClear(avatar.UUID);
if (closeChildAgents && !isChildAgent)
{
List<ulong> regions = avatar.KnownRegionHandles;
regions.Remove(RegionInfo.RegionHandle);
m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
}
m_eventManager.TriggerClientClosed(agentID, this);
m_eventManager.TriggerOnRemovePresence(agentID);
if (!isChildAgent)
{
if (AttachmentsModule != null)
{
AttachmentsModule.DeRezAttachments(avatar);
}
ForEachClient(
delegate(IClientAPI client)
{
//We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
catch (NullReferenceException) { }
});
}
// It's possible for child agents to have transactions if changes are being made cross-border.
if (AgentTransactionsModule != null)
AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode);
}
catch (Exception e)
{
m_log.Error(
string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
}
finally
{
try
{
// Always clean these structures up so that any failure above doesn't cause them to remain in the
// scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
// the same cleanup exception continually.
m_sceneGraph.RemoveScenePresence(agentID);
m_clientManager.Remove(agentID);
isChildAgent = avatar.IsChildAgent;
m_log.DebugFormat(
"[SCENE]: Removing {0} agent {1} {2} from {3}",
(isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
avatar.Close();
// Don't do this to root agents, it's not nice for the viewer
if (closeChildAgents && isChildAgent)
{
// Tell a single agent to disconnect from the region.
IEventQueue eq = RequestModuleInterface<IEventQueue>();
if (eq != null)
{
eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
}
else
{
avatar.ControllingClient.SendShutdownConnectionNotice();
}
}
// Only applies to root agents.
if (avatar.ParentID != 0)
{
avatar.StandUp();
}
m_sceneGraph.removeUserCount(!isChildAgent);
// TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
// unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
if (closeChildAgents && CapsModule != null)
CapsModule.RemoveCaps(agentID);
// REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
// this method is doing is HORRIBLE!!!
avatar.Scene.NeedSceneCacheClear(avatar.UUID);
if (closeChildAgents && !isChildAgent)
{
List<ulong> regions = avatar.KnownRegionHandles;
regions.Remove(RegionInfo.RegionHandle);
m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
}
m_eventManager.TriggerClientClosed(agentID, this);
m_eventManager.TriggerOnRemovePresence(agentID);
if (!isChildAgent)
{
if (AttachmentsModule != null)
{
AttachmentsModule.DeRezAttachments(avatar);
}
ForEachClient(
delegate(IClientAPI client)
{
//We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
catch (NullReferenceException) { }
});
}
// It's possible for child agents to have transactions if changes are being made cross-border.
if (AgentTransactionsModule != null)
AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
}
catch (Exception e)
{
m_log.Error(
string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e);
string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
}
finally
{
try
{
// Always clean these structures up so that any failure above doesn't cause them to remain in the
// scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
// the same cleanup exception continually.
m_sceneGraph.RemoveScenePresence(agentID);
m_clientManager.Remove(agentID);
avatar.Close();
}
catch (Exception e)
{
m_log.Error(
string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e);
}
}
}
@@ -3412,11 +3490,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Do the work necessary to initiate a new user connection for a particular scene.
/// At the moment, this consists of setting up the caps infrastructure
/// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it let, we allowed banned users in still.
/// </summary>
/// <param name="agent">CircuitData of the agent who is connecting</param>
/// <param name="teleportFlags"></param>
/// <param name="reason">Outputs the reason for the false response on this string</param>
/// <returns>True if the region accepts this agent. False if it does not. False will
/// also return a reason.</returns>
@@ -3427,10 +3503,20 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Do the work necessary to initiate a new user connection for a particular scene.
/// At the moment, this consists of setting up the caps infrastructure
/// </summary>
/// <remarks>
/// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it yet, we still allowed banned users in.
///
/// At the moment this method consists of setting up the caps infrastructure
/// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it let, we allowed banned users in still.
/// </summary>
///
/// This method is called by the login service (in the case of login) or another simulator (in the case of region
/// cross or teleport) to initiate the connection. It is not triggered by the viewer itself - the connection
/// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of
/// the LLUDP stack).
/// </remarks>
/// <param name="agent">CircuitData of the agent who is connecting</param>
/// <param name="reason">Outputs the reason for the false response on this string</param>
/// <param name="requirePresenceLookup">True for normal presence. False for NPC
@@ -3515,88 +3601,97 @@ namespace OpenSim.Region.Framework.Scenes
agent.firstname, agent.lastname, agent.Viewer);
reason = "Access denied, your viewer is banned by the region owner";
return false;
}
ScenePresence sp = GetScenePresence(agent.AgentID);
if (sp != null && !sp.IsChildAgent)
{
// We have a zombie from a crashed session.
// Or the same user is trying to be root twice here, won't work.
// Kill it.
m_log.WarnFormat(
"[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
sp.Name, sp.UUID, RegionInfo.RegionName);
sp.ControllingClient.Close();
sp = null;
}
ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
ILandObject land;
//On login test land permisions
if (vialogin)
lock (agent)
{
if (land != null && !TestLandRestrictions(agent, land, out reason))
ScenePresence sp = GetScenePresence(agent.AgentID);
if (sp != null && !sp.IsChildAgent)
{
return false;
// We have a zombie from a crashed session.
// Or the same user is trying to be root twice here, won't work.
// Kill it.
m_log.WarnFormat(
"[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
sp.Name, sp.UUID, RegionInfo.RegionName);
sp.ControllingClient.Close(true);
sp = null;
}
}
if (sp == null) // We don't have an [child] agent here already
{
if (requirePresenceLookup)
land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
//On login test land permisions
if (vialogin)
{
try
if (land != null && !TestLandRestrictions(agent, land, out reason))
{
if (!VerifyUserPresence(agent, out reason))
return false;
} catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
return false;
}
}
try
if (sp == null) // We don't have an [child] agent here already
{
if (!AuthorizeUser(agent, out reason))
if (requirePresenceLookup)
{
try
{
if (!VerifyUserPresence(agent, out reason))
return false;
}
catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
return false;
}
}
try
{
if (!AuthorizeUser(agent, out reason))
return false;
}
catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
return false;
} catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
return false;
}
m_log.InfoFormat(
"[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
agent.AgentID, agent.circuitcode);
if (CapsModule != null)
{
CapsModule.SetAgentCapsSeeds(agent);
CapsModule.CreateCaps(agent.AgentID);
}
} else
{
// Let the SP know how we got here. This has a lot of interesting
// uses down the line.
sp.TeleportFlags = (TPFlags)teleportFlags;
if (sp.IsChildAgent)
{
m_log.DebugFormat(
"[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
agent.AgentID, RegionInfo.RegionName);
sp.AdjustKnownSeeds();
}
m_log.InfoFormat(
"[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
agent.AgentID, agent.circuitcode);
if (CapsModule != null)
{
CapsModule.SetAgentCapsSeeds(agent);
CapsModule.CreateCaps(agent.AgentID);
}
}
else
{
// Let the SP know how we got here. This has a lot of interesting
// uses down the line.
sp.TeleportFlags = (TPFlags)teleportFlags;
if (sp.IsChildAgent)
{
m_log.DebugFormat(
"[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
agent.AgentID, RegionInfo.RegionName);
sp.AdjustKnownSeeds();
if (CapsModule != null)
CapsModule.SetAgentCapsSeeds(agent);
}
}
}
@@ -3998,8 +4093,9 @@ namespace OpenSim.Region.Framework.Scenes
return false;
}
// We have to wait until the viewer contacts this region after receiving EAC.
// That calls AddNewClient, which finally creates the ScenePresence
// We have to wait until the viewer contacts this region
// after receiving the EnableSimulator HTTP Event Queue message. This triggers the viewer to send
// a UseCircuitCode packet which in turn calls AddNewClient which finally creates the ScenePresence.
ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID);
if (childAgentUpdate != null)
@@ -4083,16 +4179,19 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Tell a single agent to disconnect from the region.
/// </summary>
/// <param name="regionHandle"></param>
/// <param name="agentID"></param>
public bool IncomingCloseAgent(UUID agentID)
/// <param name="force">
/// Force the agent to close even if it might be in the middle of some other operation. You do not want to
/// force unless you are absolutely sure that the agent is dead and a normal close is not working.
/// </param>
public bool IncomingCloseAgent(UUID agentID, bool force)
{
//m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID);
ScenePresence presence = m_sceneGraph.GetScenePresence(agentID);
if (presence != null)
{
presence.ControllingClient.Close();
presence.ControllingClient.Close(force);
return true;
}
@@ -4545,6 +4644,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)
@@ -4576,6 +4687,18 @@ namespace OpenSim.Region.Framework.Scenes
return m_sceneGraph.GetSceneObjectPart(fullID);
}
/// <summary>
/// Attempt to get a prim via its UUID
/// </summary>
/// <param name="fullID"></param>
/// <param name="sop"></param>
/// <returns></returns>
public bool TryGetSceneObjectPart(UUID fullID, out SceneObjectPart sop)
{
sop = GetSceneObjectPart(fullID);
return sop != null;
}
/// <summary>
/// Get a scene object group that contains the prim with the given local id
/// </summary>

View File

@@ -300,7 +300,7 @@ namespace OpenSim.Region.Framework.Scenes
public bool AddNewSceneObject(
SceneObjectGroup sceneObject, bool attachToBackup, Vector3? pos, Quaternion? rot, Vector3 vel)
{
AddNewSceneObject(sceneObject, true, false);
AddNewSceneObject(sceneObject, attachToBackup, false);
if (pos != null)
sceneObject.AbsolutePosition = (Vector3)pos;

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.
@@ -2674,27 +2666,23 @@ namespace OpenSim.Region.Framework.Scenes
RootPart.StoreUndoState(true);
scale.X = Math.Min(scale.X, Scene.m_maxNonphys);
scale.Y = Math.Min(scale.Y, Scene.m_maxNonphys);
scale.Z = Math.Min(scale.Z, Scene.m_maxNonphys);
PhysicsActor pa = m_rootPart.PhysActor;
if (pa != null && pa.IsPhysical)
if (Scene != null)
{
scale.X = Math.Min(scale.X, Scene.m_maxPhys);
scale.Y = Math.Min(scale.Y, Scene.m_maxPhys);
scale.Z = Math.Min(scale.Z, Scene.m_maxPhys);
}
PhysicsActor pa = m_rootPart.PhysActor;
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];
@@ -2708,7 +2696,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;
@@ -2717,7 +2705,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;
@@ -2726,7 +2714,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;
@@ -2737,7 +2725,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;
@@ -2746,7 +2734,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;
@@ -2755,7 +2743,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;
@@ -2779,7 +2767,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;
@@ -733,7 +733,7 @@ namespace OpenSim.Region.Framework.Scenes
}
catch (Exception e)
{
m_log.Error("[SCENEOBJECTPART]: GROUP POSITION. " + e.Message);
m_log.ErrorFormat("[SCENEOBJECTPART]: GROUP POSITION. {0}", e);
}
}
@@ -2368,17 +2368,20 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="scale"></param>
public void Resize(Vector3 scale)
{
scale.X = Math.Min(scale.X, ParentGroup.Scene.m_maxNonphys);
scale.Y = Math.Min(scale.Y, ParentGroup.Scene.m_maxNonphys);
scale.Z = Math.Min(scale.Z, ParentGroup.Scene.m_maxNonphys);
if (ParentGroup.Scene != null)
{
scale.X = Math.Min(scale.X, ParentGroup.Scene.m_maxNonphys);
scale.Y = Math.Min(scale.Y, ParentGroup.Scene.m_maxNonphys);
scale.Z = Math.Min(scale.Z, ParentGroup.Scene.m_maxNonphys);
PhysicsActor pa = PhysActor;
PhysicsActor pa = PhysActor;
if (pa != null && pa.IsPhysical)
{
scale.X = Math.Min(scale.X, ParentGroup.Scene.m_maxPhys);
scale.Y = Math.Min(scale.Y, ParentGroup.Scene.m_maxPhys);
scale.Z = Math.Min(scale.Z, ParentGroup.Scene.m_maxPhys);
if (pa != null && pa.IsPhysical)
{
scale.X = Math.Min(scale.X, ParentGroup.Scene.m_maxPhys);
scale.Y = Math.Min(scale.Y, ParentGroup.Scene.m_maxPhys);
scale.Z = Math.Min(scale.Z, ParentGroup.Scene.m_maxPhys);
}
}
// m_log.DebugFormat("[SCENE OBJECT PART]: Resizing {0} {1} to {2}", Name, LocalId, scale);
@@ -3150,61 +3153,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>
@@ -3229,21 +3233,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);
}
}
@@ -3263,20 +3270,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

@@ -891,7 +891,9 @@ namespace OpenSim.Region.Framework.Scenes
{
if (wasChild && HasAttachments())
{
m_log.DebugFormat("[SCENE PRESENCE]: Restarting scripts in attachments...");
m_log.DebugFormat(
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
// Resume scripts
foreach (SceneObjectGroup sog in m_attachments)
{

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

@@ -141,7 +141,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
TestScene scene = new SceneHelpers().SetupScene();
ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1));
scene.IncomingCloseAgent(sp.UUID);
scene.IncomingCloseAgent(sp.UUID, false);
Assert.That(scene.GetScenePresence(sp.UUID), Is.Null);
Assert.That(scene.AuthenticateHandler.GetAgentCircuitData(sp.UUID), Is.Null);

View File

@@ -50,8 +50,40 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Tests
{
[TestFixture]
public class UserInventoryTests
public class UserInventoryTests : OpenSimTestCase
{
[Test]
public void TestCreateInventoryFolders()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
// For this test both folders will have the same name which is legal in SL user inventories.
string foldersName = "f1";
Scene scene = new SceneHelpers().SetupScene();
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene, TestHelpers.ParseTail(1001));
UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, user1.PrincipalID, foldersName);
List<InventoryFolderBase> oneFolder
= UserInventoryHelpers.GetInventoryFolders(scene.InventoryService, user1.PrincipalID, foldersName);
Assert.That(oneFolder.Count, Is.EqualTo(1));
InventoryFolderBase firstRetrievedFolder = oneFolder[0];
Assert.That(firstRetrievedFolder.Name, Is.EqualTo(foldersName));
UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, user1.PrincipalID, foldersName);
List<InventoryFolderBase> twoFolders
= UserInventoryHelpers.GetInventoryFolders(scene.InventoryService, user1.PrincipalID, foldersName);
Assert.That(twoFolders.Count, Is.EqualTo(2));
Assert.That(twoFolders[0].Name, Is.EqualTo(foldersName));
Assert.That(twoFolders[1].Name, Is.EqualTo(foldersName));
Assert.That(twoFolders[0].ID, Is.Not.EqualTo(twoFolders[1].ID));
}
[Test]
public void TestGiveInventoryItem()
{
@@ -83,7 +115,7 @@ namespace OpenSim.Region.Framework.Tests
public void TestGiveInventoryFolder()
{
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
// TestHelpers.EnableLogging();
Scene scene = new SceneHelpers().SetupScene();
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene, TestHelpers.ParseTail(1001));

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>

View File

@@ -885,6 +885,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
}
public void Close()
{
Close(false);
}
public void Close(bool force)
{
Disconnect();
}

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

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

@@ -900,6 +900,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC
}
public void Close()
{
Close(false);
}
public void Close(bool force)
{
// Remove ourselves from the scene
m_scene.RemoveClient(AgentId, false);

View File

@@ -176,16 +176,17 @@ namespace OpenSim.Region.OptionalModules.World.NPC
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
if (scene.TryGetScenePresence(agentID, out sp))
{
m_log.DebugFormat(
"[NPC MODULE]: Moving {0} to {1} in {2}, noFly {3}, landAtTarget {4}",
sp.Name, pos, scene.RegionInfo.RegionName, noFly, landAtTarget);
m_log.DebugFormat(
"[NPC MODULE]: Moving {0} to {1} in {2}, noFly {3}, landAtTarget {4}",
sp.Name, pos, scene.RegionInfo.RegionName, noFly, landAtTarget);
sp.MoveToTarget(pos, noFly, landAtTarget);
sp.SetAlwaysRun = running;
return true;
sp.MoveToTarget(pos, noFly, landAtTarget);
sp.SetAlwaysRun = running;
return true;
}
}
}
@@ -199,12 +200,13 @@ namespace OpenSim.Region.OptionalModules.World.NPC
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
if (scene.TryGetScenePresence(agentID, out sp))
{
sp.Velocity = Vector3.Zero;
sp.ResetMoveToTarget();
sp.Velocity = Vector3.Zero;
sp.ResetMoveToTarget();
return true;
return true;
}
}
}
@@ -222,9 +224,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
m_avatars[agentID].Say(channel, text);
return true;
@@ -240,9 +239,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
m_avatars[agentID].Shout(channel, text);
return true;
@@ -259,11 +255,13 @@ namespace OpenSim.Region.OptionalModules.World.NPC
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
sp.HandleAgentRequestSit(m_avatars[agentID], agentID, partID, Vector3.Zero);
// sp.HandleAgentSit(m_avatars[agentID], agentID);
return true;
if (scene.TryGetScenePresence(agentID, out sp))
{
sp.HandleAgentRequestSit(m_avatars[agentID], agentID, partID, Vector3.Zero);
// sp.HandleAgentSit(m_avatars[agentID], agentID);
return true;
}
}
}
@@ -276,9 +274,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
m_avatars[agentID].Whisper(channel, text);
return true;
@@ -295,10 +290,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC
if (m_avatars.ContainsKey(agentID))
{
ScenePresence sp;
scene.TryGetScenePresence(agentID, out sp);
sp.StandUp();
if (scene.TryGetScenePresence(agentID, out sp))
{
sp.StandUp();
return true;
return true;
}
}
}
@@ -311,6 +308,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
if (m_avatars.ContainsKey(agentID))
return m_avatars[agentID].Touch(objectID);
return false;
}
}
@@ -321,9 +319,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
NPCAvatar av;
if (m_avatars.TryGetValue(agentID, out av))
{
return av.OwnerID;
}
}
return UUID.Zero;

View File

@@ -183,6 +183,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llResetScript()
{
m_host.AddScriptLPS(1);
// We need to tell the URL module, if we hav one, to release
// the allocated URLs
if (m_UrlModule != null)
m_UrlModule.ScriptRemoved(m_item.ItemID);
m_ScriptEngine.ApiResetScript(m_item.ItemID);
}
@@ -237,33 +243,38 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
public List<SceneObjectPart> GetLinkParts(int linkType)
{
return GetLinkParts(m_host, linkType);
}
private List<SceneObjectPart> GetLinkParts(SceneObjectPart part, int linkType)
{
List<SceneObjectPart> ret = new List<SceneObjectPart>();
ret.Add(m_host);
ret.Add(part);
switch (linkType)
{
case ScriptBaseClass.LINK_SET:
return new List<SceneObjectPart>(m_host.ParentGroup.Parts);
return new List<SceneObjectPart>(part.ParentGroup.Parts);
case ScriptBaseClass.LINK_ROOT:
ret = new List<SceneObjectPart>();
ret.Add(m_host.ParentGroup.RootPart);
ret.Add(part.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_ALL_OTHERS:
ret = new List<SceneObjectPart>(m_host.ParentGroup.Parts);
ret = new List<SceneObjectPart>(part.ParentGroup.Parts);
if (ret.Contains(m_host))
ret.Remove(m_host);
if (ret.Contains(part))
ret.Remove(part);
return ret;
case ScriptBaseClass.LINK_ALL_CHILDREN:
ret = new List<SceneObjectPart>(m_host.ParentGroup.Parts);
ret = new List<SceneObjectPart>(part.ParentGroup.Parts);
if (ret.Contains(m_host.ParentGroup.RootPart))
ret.Remove(m_host.ParentGroup.RootPart);
if (ret.Contains(part.ParentGroup.RootPart))
ret.Remove(part.ParentGroup.RootPart);
return ret;
case ScriptBaseClass.LINK_THIS:
@@ -273,7 +284,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (linkType < 0)
return new List<SceneObjectPart>();
SceneObjectPart target = m_host.ParentGroup.GetLinkNumPart(linkType);
SceneObjectPart target = part.ParentGroup.GetLinkNumPart(linkType);
if (target == null)
return new List<SceneObjectPart>();
ret = new List<SceneObjectPart>();
@@ -1957,7 +1968,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
pos.x > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a east-adjacent region.
pos.y < -10.0 || // return FALSE if more than 10 meters into a south-adjacent region.
pos.y > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a north-adjacent region.
pos.z > 4096 // return FALSE if altitude than 4096m
pos.z > Constants.RegionHeight // return FALSE if altitude than 4096m
)
)
{
@@ -4012,9 +4023,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llSetText(string text, LSL_Vector color, double alpha)
{
m_host.AddScriptLPS(1);
Vector3 av3 = new Vector3(Util.Clip((float)color.x, 0.0f, 1.0f),
Util.Clip((float)color.y, 0.0f, 1.0f),
Util.Clip((float)color.z, 0.0f, 1.0f));
Vector3 av3 = Util.Clip(new Vector3((float)color.x, (float)color.y,
(float)color.z), 0.0f, 1.0f);
m_host.SetText(text.Length > 254 ? text.Remove(254) : text, av3, Util.Clip((float)alpha, 0.0f, 1.0f));
//m_host.ParentGroup.HasGroupChanged = true;
//m_host.ParentGroup.ScheduleGroupForFullUpdate();
@@ -5122,17 +5132,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
string ret = String.Empty;
int x = 0;
m_host.AddScriptLPS(1);
if (src.Data.Length > 0)
{
ret = src.Data[x++].ToString();
for (; x < src.Data.Length; x++)
{
ret += ", "+src.Data[x].ToString();
}
ret = string.Join(", ",
(new List<object>(src.Data)).ConvertAll<string>(o =>
{
return o.ToString();
}).ToArray());
}
return ret;
@@ -5427,27 +5436,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// Returns the index of the first occurrence of test
/// in src.
/// </summary>
/// <param name="src">Source list</param>
/// <param name="test">List to search for</param>
/// <returns>
/// The index number of the point in src where test was found if it was found.
/// Otherwise returns -1
/// </returns>
public LSL_Integer llListFindList(LSL_List src, LSL_List test)
{
int index = -1;
int length = src.Length - test.Length + 1;
m_host.AddScriptLPS(1);
// If either list is empty, do not match
if (src.Length != 0 && test.Length != 0)
{
for (int i = 0; i < length; i++)
{
if (src.Data[i].Equals(test.Data[0]))
// Why this piece of insanity? This is because most script constants are C# value types (e.g. int)
// rather than wrapped LSL types. Such a script constant does not have int.Equal(LSL_Integer) code
// and so the comparison fails even if the LSL_Integer conceptually has the same value.
// Therefore, here we test Equals on both the source and destination objects.
// However, a future better approach may be use LSL struct script constants (e.g. LSL_Integer(1)).
if (src.Data[i].Equals(test.Data[0]) || test.Data[0].Equals(src.Data[i]))
{
int j;
for (j = 1; j < test.Length; j++)
if (!src.Data[i+j].Equals(test.Data[j]))
if (!(src.Data[i+j].Equals(test.Data[j]) || test.Data[j].Equals(src.Data[i+j])))
break;
if (j == test.Length)
{
index = i;
@@ -5458,19 +5476,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
return index;
}
public LSL_String llGetObjectName()
{
m_host.AddScriptLPS(1);
return m_host.Name!=null?m_host.Name:String.Empty;
return m_host.Name !=null ? m_host.Name : String.Empty;
}
public void llSetObjectName(string name)
{
m_host.AddScriptLPS(1);
m_host.Name = name!=null?name:String.Empty;
m_host.Name = name != null ? name : String.Empty;
}
public LSL_String llGetDate()
@@ -7178,7 +7195,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llSetPrimitiveParams(LSL_List rules)
{
m_host.AddScriptLPS(1);
SetPrimParams(m_host, rules);
setLinkPrimParams(ScriptBaseClass.LINK_THIS, rules);
ScriptSleep(200);
}
@@ -7203,11 +7221,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
List<SceneObjectPart> parts = GetLinkParts(linknumber);
LSL_List remaining = null;
foreach (SceneObjectPart part in parts)
SetPrimParams(part, rules);
remaining = SetPrimParams(part, rules);
while(remaining != null && remaining.Length > 2)
{
linknumber = remaining.GetLSLIntegerItem(0);
rules = remaining.GetSublist(1,-1);
parts = GetLinkParts(linknumber);
foreach (SceneObjectPart part in parts)
remaining = SetPrimParams(part, rules);
}
}
protected void SetPrimParams(SceneObjectPart part, LSL_List rules)
protected LSL_List SetPrimParams(SceneObjectPart part, LSL_List rules)
{
int idx = 0;
@@ -7230,7 +7260,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_POSITION:
case (int)ScriptBaseClass.PRIM_POS_LOCAL:
if (remain < 1)
return;
return null;
v=rules.GetVector3Item(idx++);
positionChanged = true;
@@ -7239,7 +7269,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
break;
case (int)ScriptBaseClass.PRIM_SIZE:
if (remain < 1)
return;
return null;
v=rules.GetVector3Item(idx++);
SetScale(part, v);
@@ -7247,7 +7277,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
break;
case (int)ScriptBaseClass.PRIM_ROTATION:
if (remain < 1)
return;
return null;
LSL_Rotation q = rules.GetQuaternionItem(idx++);
// try to let this work as in SL...
@@ -7267,7 +7297,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE:
if (remain < 3)
return;
return null;
code = (int)rules.GetLSLIntegerItem(idx++);
@@ -7286,7 +7316,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
case (int)ScriptBaseClass.PRIM_TYPE_BOX:
if (remain < 6)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++);
v = rules.GetVector3Item(idx++); // cut
@@ -7301,7 +7331,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_CYLINDER:
if (remain < 6)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); // cut
@@ -7315,7 +7345,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_PRISM:
if (remain < 6)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
@@ -7329,7 +7359,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_SPHERE:
if (remain < 5)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); // cut
@@ -7342,7 +7372,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_TORUS:
if (remain < 11)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
@@ -7361,7 +7391,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_TUBE:
if (remain < 11)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
@@ -7380,7 +7410,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_RING:
if (remain < 11)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++); // holeshape
v = rules.GetVector3Item(idx++); //cut
@@ -7399,7 +7429,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TYPE_SCULPT:
if (remain < 2)
return;
return null;
string map = rules.Data[idx++].ToString();
face = (int)rules.GetLSLIntegerItem(idx++); // type
@@ -7411,7 +7441,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TEXTURE:
if (remain < 5)
return;
return null;
face=(int)rules.GetLSLIntegerItem(idx++);
string tex=rules.Data[idx++].ToString();
@@ -7428,7 +7458,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_COLOR:
if (remain < 3)
return;
return null;
face=(int)rules.GetLSLIntegerItem(idx++);
LSL_Vector color=rules.GetVector3Item(idx++);
@@ -7441,7 +7471,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_FLEXIBLE:
if (remain < 7)
return;
return null;
bool flexi = rules.GetLSLIntegerItem(idx++);
int softness = rules.GetLSLIntegerItem(idx++);
@@ -7457,7 +7487,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_POINT_LIGHT:
if (remain < 5)
return;
return null;
bool light = rules.GetLSLIntegerItem(idx++);
LSL_Vector lightcolor = rules.GetVector3Item(idx++);
float intensity = (float)rules.GetLSLFloatItem(idx++);
@@ -7470,7 +7500,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_GLOW:
if (remain < 2)
return;
return null;
face = rules.GetLSLIntegerItem(idx++);
float glow = (float)rules.GetLSLFloatItem(idx++);
@@ -7480,7 +7510,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_BUMP_SHINY:
if (remain < 3)
return;
return null;
face = (int)rules.GetLSLIntegerItem(idx++);
int shiny = (int)rules.GetLSLIntegerItem(idx++);
Bumpiness bump = (Bumpiness)(int)rules.GetLSLIntegerItem(idx++);
@@ -7491,7 +7521,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_FULLBRIGHT:
if (remain < 2)
return;
return null;
face = rules.GetLSLIntegerItem(idx++);
bool st = rules.GetLSLIntegerItem(idx++);
SetFullBright(part, face , st);
@@ -7499,17 +7529,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_MATERIAL:
if (remain < 1)
return;
return null;
int mat = rules.GetLSLIntegerItem(idx++);
if (mat < 0 || mat > 7)
return;
return null;
part.Material = Convert.ToByte(mat);
break;
case (int)ScriptBaseClass.PRIM_PHANTOM:
if (remain < 1)
return;
return null;
string ph = rules.Data[idx++].ToString();
m_host.ParentGroup.ScriptSetPhantomStatus(ph.Equals("1"));
@@ -7518,7 +7548,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_PHYSICS:
if (remain < 1)
return;
return null;
string phy = rules.Data[idx++].ToString();
bool physics;
@@ -7532,7 +7562,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TEMP_ON_REZ:
if (remain < 1)
return;
return null;
string temp = rules.Data[idx++].ToString();
m_host.ParentGroup.ScriptSetTemporaryStatus(temp.Equals("1"));
@@ -7541,7 +7571,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case (int)ScriptBaseClass.PRIM_TEXGEN:
if (remain < 2)
return;
return null;
//face,type
face = rules.GetLSLIntegerItem(idx++);
int style = rules.GetLSLIntegerItem(idx++);
@@ -7549,37 +7579,37 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
break;
case (int)ScriptBaseClass.PRIM_TEXT:
if (remain < 3)
return;
return null;
string primText = rules.GetLSLStringItem(idx++);
LSL_Vector primTextColor = rules.GetVector3Item(idx++);
LSL_Float primTextAlpha = rules.GetLSLFloatItem(idx++);
Vector3 av3 = new Vector3(Util.Clip((float)primTextColor.x, 0.0f, 1.0f),
Util.Clip((float)primTextColor.y, 0.0f, 1.0f),
Util.Clip((float)primTextColor.z, 0.0f, 1.0f));
Vector3 av3 = Util.Clip(new Vector3((float)primTextColor.x,
(float)primTextColor.y,
(float)primTextColor.z), 0.0f, 1.0f);
part.SetText(primText, av3, Util.Clip((float)primTextAlpha, 0.0f, 1.0f));
break;
case (int)ScriptBaseClass.PRIM_NAME:
if (remain < 1)
return;
return null;
string primName = rules.GetLSLStringItem(idx++);
part.Name = primName;
break;
case (int)ScriptBaseClass.PRIM_DESC:
if (remain < 1)
return;
return null;
string primDesc = rules.GetLSLStringItem(idx++);
part.Description = primDesc;
break;
case (int)ScriptBaseClass.PRIM_ROT_LOCAL:
if (remain < 1)
return;
return null;
LSL_Rotation lr = rules.GetQuaternionItem(idx++);
SetRot(part, Rot2Quaternion(lr));
break;
case (int)ScriptBaseClass.PRIM_OMEGA:
if (remain < 3)
return;
return null;
LSL_Vector axis = rules.GetVector3Item(idx++);
LSL_Float spinrate = rules.GetLSLFloatItem(idx++);
LSL_Float gain = rules.GetLSLFloatItem(idx++);
@@ -7587,12 +7617,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
break;
case (int)ScriptBaseClass.PRIM_LINK_TARGET:
if (remain < 3) // setting to 3 on the basis that parsing any usage of PRIM_LINK_TARGET that has nothing following it is pointless.
return;
LSL_Integer new_linknumber = rules.GetLSLIntegerItem(idx++);
LSL_List new_rules = rules.GetSublist(idx, -1);
setLinkPrimParams((int)new_linknumber, new_rules);
return null;
return;
return rules.GetSublist(idx, -1);
}
}
}
@@ -7614,6 +7641,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
}
}
return null;
}
public LSL_String llStringToBase64(string str)
@@ -10686,7 +10714,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (obj.OwnerID != m_host.OwnerID)
return;
SetPrimParams(obj, rules);
LSL_List remaining = SetPrimParams(obj, rules);
while (remaining != null && remaining.Length > 2)
{
LSL_Integer newLink = remaining.GetLSLIntegerItem(0);
LSL_List newrules = remaining.GetSublist(1, -1);
foreach(SceneObjectPart part in GetLinkParts(obj, newLink)){
remaining = SetPrimParams(part, newrules);
}
}
}
public LSL_List GetLinkPrimitiveParamsEx(LSL_Key prim, LSL_List rules)

View File

@@ -2434,8 +2434,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (!npcModule.CheckPermissions(npcId, m_host.OwnerID))
return new LSL_Vector(0, 0, 0);
Vector3 pos = World.GetScenePresence(npcId).AbsolutePosition;
return new LSL_Vector(pos.X, pos.Y, pos.Z);
ScenePresence sp = World.GetScenePresence(npcId);
if (sp != null)
{
Vector3 pos = sp.AbsolutePosition;
return new LSL_Vector(pos.X, pos.Y, pos.Z);
}
}
return new LSL_Vector(0, 0, 0);
@@ -2503,9 +2508,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W);
ScenePresence sp = World.GetScenePresence(npcId);
Quaternion rot = sp.Rotation;
return new LSL_Rotation(rot.X, rot.Y, rot.Z, rot.W);
if (sp != null)
{
Quaternion rot = sp.Rotation;
return new LSL_Rotation(rot.X, rot.Y, rot.Z, rot.W);
}
}
return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W);
@@ -2527,7 +2535,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return;
ScenePresence sp = World.GetScenePresence(npcId);
sp.Rotation = LSL_Api.Rot2Quaternion(rotation);
if (sp != null)
sp.Rotation = LSL_Api.Rot2Quaternion(rotation);
}
}
@@ -2689,6 +2699,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
CheckThreatLevel(ThreatLevel.High, "osNpcTouch");
m_host.AddScriptLPS(1);
INPCModule module = World.RequestModuleInterface<INPCModule>();
int linkNum = link_num.value;
if (module != null || (linkNum < 0 && linkNum != ScriptBaseClass.LINK_THIS))
@@ -2696,12 +2707,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
UUID npcId;
if (!UUID.TryParse(npcLSL_Key, out npcId) || !module.CheckPermissions(npcId, m_host.OwnerID))
return;
SceneObjectPart part = null;
UUID objectId;
if (UUID.TryParse(LSL_String.ToString(object_key), out objectId))
part = World.GetSceneObjectPart(objectId);
if (part == null)
return;
if (linkNum != ScriptBaseClass.LINK_THIS)
{
if (linkNum == 0 || linkNum == ScriptBaseClass.LINK_ROOT)
@@ -2716,6 +2730,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return;
}
}
module.Touch(npcId, part.UUID);
}
}
@@ -2866,7 +2881,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
avatar.SpeedModifier = (float)SpeedModifier;
}
public void osKickAvatar(string FirstName,string SurName,string alert)
public void osKickAvatar(string FirstName, string SurName, string alert)
{
CheckThreatLevel(ThreatLevel.Severe, "osKickAvatar");
m_host.AddScriptLPS(1);
@@ -2880,7 +2895,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
sp.ControllingClient.Kick(alert);
// ...and close on our side
sp.Scene.IncomingCloseAgent(sp.UUID);
sp.Scene.IncomingCloseAgent(sp.UUID, false);
}
});
}

View File

@@ -40,16 +40,75 @@ using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
{
/// <summary>
/// To permit region owners to enable the extended scripting functionality
/// of OSSL, without allowing malicious scripts to access potentially
/// troublesome functions, each OSSL function is assigned a threat level,
/// and access to the functions is granted or denied based on a default
/// threshold set in OpenSim.ini (which can be overridden for individual
/// functions on a case-by-case basis)
/// </summary>
public enum ThreatLevel
{
// Not documented, presumably means permanently disabled ?
NoAccess = -1,
/// <summary>
/// Function is no threat at all. It doesn't constitute a threat to
/// either users or the system and has no known side effects.
/// </summary>
None = 0,
/// <summary>
/// Abuse of this command can cause a nuisance to the region operator,
/// such as log message spew.
/// </summary>
Nuisance = 1,
/// <summary>
/// Extreme levels of abuse of this function can cause impaired
/// functioning of the region, or very gullible users can be tricked
/// into experiencing harmless effects.
/// </summary>
VeryLow = 2,
/// <summary>
/// Intentional abuse can cause crashes or malfunction under certain
/// circumstances, which can be easily rectified; or certain users can
/// be tricked into certain situations in an avoidable manner.
/// </summary>
Low = 3,
/// <summary>
/// Intentional abuse can cause denial of service and crashes with
/// potential of data or state loss; or trusting users can be tricked
/// into embarrassing or uncomfortable situations.
/// </summary>
Moderate = 4,
/// <summary>
/// Casual abuse can cause impaired functionality or temporary denial
/// of service conditions. Intentional abuse can easily cause crashes
/// with potential data loss, or can be used to trick experienced and
/// cautious users into unwanted situations, or changes global data
/// permanently and without undo ability.
/// </summary>
High = 5,
/// <summary>
/// Even normal use may, depending on the number of instances, or
/// frequency of use, result in severe service impairment or crash
/// with loss of data, or can be used to cause unwanted or harmful
/// effects on users without giving the user a means to avoid it.
/// </summary>
VeryHigh = 6,
/// <summary>
/// Even casual use is a danger to region stability, or function allows
/// console or OS command execution, or function allows taking money
/// without consent, or allows deletion or modification of user data,
/// or allows the compromise of sensitive data by design.
/// </summary>
Severe = 7
};

View File

@@ -226,6 +226,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int ATTACH_BELLY = 28;
public const int ATTACH_RPEC = 29;
public const int ATTACH_LPEC = 30;
public const int ATTACH_LEFT_PEC = 29; // Same value as ATTACH_RPEC, see https://jira.secondlife.com/browse/SVC-580
public const int ATTACH_RIGHT_PEC = 30; // Same value as ATTACH_LPEC, see https://jira.secondlife.com/browse/SVC-580
public const int ATTACH_HUD_CENTER_2 = 31;
public const int ATTACH_HUD_TOP_RIGHT = 32;
public const int ATTACH_HUD_TOP_CENTER = 33;

View File

@@ -1064,7 +1064,7 @@ namespace OpenSim.Region.ScriptEngine.Shared
{
list ret = new list();
double entry;
for (int i = 0; i < src.Data.Length - 1; i++)
for (int i = 0; i < src.Data.Length; i++)
{
if (double.TryParse(src.Data[i].ToString(), NumberStyles.Float, Culture.NumberFormatInfo, out entry))
{

View File

@@ -0,0 +1,134 @@
/*
* 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 NUnit.Framework;
using OpenSim.Framework;
using OpenSim.Tests.Common;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.Framework.Scenes;
using Nini.Config;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenMetaverse;
using OpenSim.Tests.Common.Mock;
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
namespace OpenSim.Region.ScriptEngine.Shared.Tests
{
[TestFixture]
public class LSL_ApiListTests
{
private LSL_Api m_lslApi;
[SetUp]
public void SetUp()
{
IConfigSource initConfigSource = new IniConfigSource();
IConfig config = initConfigSource.AddConfig("XEngine");
config.Set("Enabled", "true");
Scene scene = new SceneHelpers().SetupScene();
SceneObjectPart part = SceneHelpers.AddSceneObject(scene).RootPart;
XEngine.XEngine engine = new XEngine.XEngine();
engine.Initialise(initConfigSource);
engine.AddRegion(scene);
m_lslApi = new LSL_Api();
m_lslApi.Initialize(engine, part, null);
}
[Test]
public void TestllListFindList()
{
TestHelpers.InMethod();
LSL_List src = new LSL_List(new LSL_Integer(1), new LSL_Integer(2), new LSL_Integer(3));
{
// Test for a single item that should be found
int result = m_lslApi.llListFindList(src, new LSL_List(new LSL_Integer(4)));
Assert.That(result, Is.EqualTo(-1));
}
{
// Test for a single item that should be found
int result = m_lslApi.llListFindList(src, new LSL_List(new LSL_Integer(2)));
Assert.That(result, Is.EqualTo(1));
}
{
// Test for a constant that should be found
int result = m_lslApi.llListFindList(src, new LSL_List(ScriptBaseClass.AGENT));
Assert.That(result, Is.EqualTo(0));
}
{
// Test for a list that should be found
int result = m_lslApi.llListFindList(src, new LSL_List(new LSL_Integer(2), new LSL_Integer(3)));
Assert.That(result, Is.EqualTo(1));
}
{
// Test for a single item not in the list
int result = m_lslApi.llListFindList(src, new LSL_List(new LSL_Integer(4)));
Assert.That(result, Is.EqualTo(-1));
}
{
// Test for something that should not be cast
int result = m_lslApi.llListFindList(src, new LSL_List(new LSL_String("4")));
Assert.That(result, Is.EqualTo(-1));
}
{
// Test for a list not in the list
int result
= m_lslApi.llListFindList(
src, new LSL_List(new LSL_Integer(2), new LSL_Integer(3), new LSL_Integer(4)));
Assert.That(result, Is.EqualTo(-1));
}
{
LSL_List srcWithConstants
= new LSL_List(new LSL_Integer(3), ScriptBaseClass.AGENT, ScriptBaseClass.OS_NPC_LAND_AT_TARGET);
// Test for constants that appears in the source list that should be found
int result
= m_lslApi.llListFindList(srcWithConstants, new LSL_List(new LSL_Integer(1), new LSL_Integer(2)));
Assert.That(result, Is.EqualTo(1));
}
}
}
}

View File

@@ -589,7 +589,19 @@ namespace OpenSim.Region.ScriptEngine.XEngine
if (m_Assemblies.ContainsKey(instance.AssetID))
{
string assembly = m_Assemblies[instance.AssetID];
instance.SaveState(assembly);
try
{
instance.SaveState(assembly);
}
catch (Exception e)
{
m_log.Error(
string.Format(
"[XEngine]: Failed final state save for script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name)
, e);
}
}
// Clear the event queue and abort the instance thread
@@ -707,7 +719,18 @@ namespace OpenSim.Region.ScriptEngine.XEngine
assembly = m_Assemblies[i.AssetID];
}
i.SaveState(assembly);
try
{
i.SaveState(assembly);
}
catch (Exception e)
{
m_log.Error(
string.Format(
"[XEngine]: Failed to save state of script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
i.PrimName, i.ScriptName, i.ItemID, i.ObjectID, World.Name)
, e);
}
}
instances.Clear();
@@ -900,6 +923,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
// This delay exists to stop mono problems where script compilation and startup would stop the sim
// working properly for the session.
System.Threading.Thread.Sleep(m_StartDelay);
m_log.InfoFormat("[XEngine]: Performing initial script startup on {0}", m_Scene.Name);
}
object[] o;
@@ -915,13 +940,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
if (m_InitialStartup)
if (scriptsStarted % 50 == 0)
m_log.InfoFormat(
"[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
"[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.Name);
}
}
if (m_InitialStartup)
m_log.InfoFormat(
"[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
"[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.Name);
// NOTE: Despite having a lockless queue, this lock is required
// to make sure there is never no compile thread while there
@@ -982,10 +1007,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
return false;
}
UUID assetID = item.AssetID;
m_log.DebugFormat(
"[XEngine] Loading script {0}.{1}, item UUID {2}, prim UUID {3} @ {4}.{5}",
part.ParentGroup.RootPart.Name, item.Name, itemID, part.UUID,
part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
//m_log.DebugFormat("[XEngine] Compiling script {0} ({1} on object {2})",
// item.Name, itemID.ToString(), part.ParentGroup.RootPart.Name);
UUID assetID = item.AssetID;
ScenePresence presence = m_Scene.GetScenePresence(item.OwnerID);
@@ -1164,10 +1191,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine
stateSource, m_MaxScriptQueue);
// if (DebugLevel >= 1)
m_log.DebugFormat(
"[XEngine] Loaded script {0}.{1}, item UUID {2}, prim UUID {3} @ {4}.{5}",
part.ParentGroup.RootPart.Name, item.Name, itemID, part.UUID,
part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
// m_log.DebugFormat(
// "[XEngine] Loaded script {0}.{1}, item UUID {2}, prim UUID {3} @ {4}.{5}",
// part.ParentGroup.RootPart.Name, item.Name, itemID, part.UUID,
// part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
if (presence != null)
{

View File

@@ -61,7 +61,7 @@ namespace OpenSim.Region.UserStatistics
/// <summary>
/// User statistics sessions keyed by agent ID
/// </summary>
private Dictionary<UUID, UserSessionID> m_sessions = new Dictionary<UUID, UserSessionID>();
private Dictionary<UUID, UserSession> m_sessions = new Dictionary<UUID, UserSession>();
private List<Scene> m_scenes = new List<Scene>();
private Dictionary<string, IStatsController> reports = new Dictionary<string, IStatsController>();
@@ -319,14 +319,18 @@ namespace OpenSim.Region.UserStatistics
private void OnMakeRootAgent(ScenePresence agent)
{
// m_log.DebugFormat(
// "[WEB STATS MODULE]: Looking for session {0} for {1} in {2}",
// agent.ControllingClient.SessionId, agent.Name, agent.Scene.Name);
lock (m_sessions)
{
UserSessionID uid;
UserSession uid;
if (!m_sessions.ContainsKey(agent.UUID))
{
UserSessionData usd = UserSessionUtil.newUserSessionData();
uid = new UserSessionID();
uid = new UserSession();
uid.name_f = agent.Firstname;
uid.name_l = agent.Lastname;
uid.session_data = usd;
@@ -411,9 +415,9 @@ namespace OpenSim.Region.UserStatistics
return String.Empty;
}
private UserSessionID ParseViewerStats(string request, UUID agentID)
private UserSession ParseViewerStats(string request, UUID agentID)
{
UserSessionID uid = new UserSessionID();
UserSession uid = new UserSession();
UserSessionData usd;
OSD message = OSDParser.DeserializeLLSDXml(request);
OSDMap mmap;
@@ -425,22 +429,25 @@ namespace OpenSim.Region.UserStatistics
if (!m_sessions.ContainsKey(agentID))
{
m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID);
return new UserSessionID();
return new UserSession();
}
uid = m_sessions[agentID];
// m_log.DebugFormat("[WEB STATS MODULE]: Got session {0} for {1}", uid.session_id, agentID);
}
else
{
// parse through the beginning to locate the session
if (message.Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
mmap = (OSDMap)message;
{
UUID sessionID = mmap["session_id"].AsUUID();
if (sessionID == UUID.Zero)
return new UserSessionID();
return new UserSession();
// search through each session looking for the owner
@@ -459,7 +466,7 @@ namespace OpenSim.Region.UserStatistics
// can't find a session
if (agentID == UUID.Zero)
{
return new UserSessionID();
return new UserSession();
}
}
}
@@ -468,12 +475,12 @@ namespace OpenSim.Region.UserStatistics
usd = uid.session_data;
if (message.Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
mmap = (OSDMap)message;
{
if (mmap["agent"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap agent_map = (OSDMap)mmap["agent"];
usd.agent_id = agentID;
usd.name_f = uid.name_f;
@@ -493,17 +500,18 @@ namespace OpenSim.Region.UserStatistics
(float)agent_map["fps"].AsReal());
if (mmap["downloads"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap downloads_map = (OSDMap)mmap["downloads"];
usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal();
usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal();
usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal();
// m_log.DebugFormat("[WEB STATS MODULE]: mmap[\"session_id\"] = [{0}]", mmap["session_id"].AsUUID());
usd.session_id = mmap["session_id"].AsUUID();
if (mmap["system"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap system_map = (OSDMap)mmap["system"];
usd.s_cpu = system_map["cpu"].AsString();
@@ -512,13 +520,13 @@ namespace OpenSim.Region.UserStatistics
usd.s_ram = system_map["ram"].AsInteger();
if (mmap["stats"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap stats_map = (OSDMap)mmap["stats"];
{
if (stats_map["failures"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap stats_failures = (OSDMap)stats_map["failures"];
usd.f_dropped = stats_failures["dropped"].AsInteger();
usd.f_failed_resends = stats_failures["failed_resends"].AsInteger();
@@ -527,18 +535,18 @@ namespace OpenSim.Region.UserStatistics
usd.f_send_packet = stats_failures["send_packet"].AsInteger();
if (stats_map["net"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap stats_net = (OSDMap)stats_map["net"];
{
if (stats_net["in"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap net_in = (OSDMap)stats_net["in"];
usd.n_in_kb = (float)net_in["kbytes"].AsReal();
usd.n_in_pk = net_in["packets"].AsInteger();
if (stats_net["out"].Type != OSDType.Map)
return new UserSessionID();
return new UserSession();
OSDMap net_out = (OSDMap)stats_net["out"];
usd.n_out_kb = (float)net_out["kbytes"].AsReal();
@@ -549,11 +557,18 @@ namespace OpenSim.Region.UserStatistics
uid.session_data = usd;
m_sessions[agentID] = uid;
// m_log.DebugFormat(
// "[WEB STATS MODULE]: Parse data for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id);
return uid;
}
private void UpdateUserStats(UserSessionID uid, SqliteConnection db)
private void UpdateUserStats(UserSession uid, SqliteConnection db)
{
// m_log.DebugFormat(
// "[WEB STATS MODULE]: Updating user stats for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id);
if (uid.session_id == UUID.Zero)
return;
@@ -740,7 +755,6 @@ VALUES
s.min_ping = ArrayMin_f(__ping);
s.max_ping = ArrayMax_f(__ping);
s.mode_ping = ArrayMode_f(__ping);
}
#region Statistics
@@ -985,7 +999,7 @@ VALUES
}
#region structs
public struct UserSessionID
public class UserSession
{
public UUID session_id;
public UUID region_id;

View File

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

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