Compare commits

...

131 Commits

Author SHA1 Message Date
Justin Clark-Casey (justincc) 8b267b5bb5 Fix bug in HG attachment throttling code where attachments were never rezzed on any teleport within a foreign grid after the first.
Bug was introduced in ghosts branch commit 69abade
2014-11-21 21:34:18 +00:00
Justin Clark-Casey (justincc) c18cecbeb7 Change jobengine logging command to "debug jobengine log <int>" rather than loglevel, in common with similar commands. 2014-11-21 21:25:55 +00:00
Justin Clark-Casey (justincc) 11ed60645a When logging reigon information returned by GateKeeperService.GetHyperlinkRegion, log the return server URL returned.
This helps diagnoses misconfiguration where, for instance, a LAN ExternalHostName has been configured that isn't reachable externally.
2014-11-21 20:36:48 +00:00
Justin Clark-Casey (justincc) 84d557a7bd Make small adjustment to JobEngine default from previous commit to enable it when [Startup] section is not present (though this is extremely unlikely). 2014-11-21 02:16:56 +00:00
Justin Clark-Casey (justincc) c96806a3f5 Add [Startup] JobEngineEnabled setting that allows the job engine to be disabled for testing purposes if necessary. 2014-11-21 01:44:30 +00:00
Justin Clark-Casey (justincc) 377b79bafc Move conditionals which control whether a task is placed in the JobEngine inside Watchdog.RunJob() (renamed from RunWhenPossible) and generalize them. 2014-11-21 01:08:58 +00:00
Justin Clark-Casey (justincc) df75f14bca Add [BulletSim] option AvatarToAvatarCollisionsByDefault to control whether avatars collide. This is true by default.
This is implemented with a new collision type (PhantomToOthersAvatar) to potentially allow colliding and non-colliding avatars to be present in the same scene.
So there is no provision yet for giving avatars different collision types.
This commit replaces the temporary change in commit f3eaa6d8 where avatars would never collide when using BulletSim
This is equivalent to the av_av_collisions_off option in ODE.
2014-11-20 21:28:12 +00:00
Justin Clark-Casey (justincc) bf5e220450 Fix compile error from previous 1d56029848 2014-11-11 18:10:51 +00:00
Justin Clark-Casey (justincc) 1d56029848 Fix issue where llRemoteLoadScriptPin() would treat 0 (the default) as a valid set pin in a destination prim rather than the unset no pin state
Adds regression test for this case.
2014-11-11 17:28:24 +00:00
Justin Clark-Casey (justincc) 6affa906ee When processing incoming attachments via HG, if a request for uuid gathering or final asset import takes too long remove remaining requests from same user to prevent hold up of other user's incoming attachments.
This improves upon the earlier naive simply queueing immplementation.
Threshold is 30 seconds.  If this happens to a user they can relog and fetch will be reattempted.
2014-11-06 01:15:22 +00:00
Justin Clark-Casey (justincc) 9d78d16cf3 Introduce an IteratingUuidGatherer where each fetch from the asset service (iteration) can be controlled by the caller.
This is to enable an imminent change where incoming HG scene object fetching can assess the time taken by each request rather than being forced to perform all requests in one call.
Soon, this will replace the existing UuidGatherer since it is both simpler and more flexible.
2014-11-05 19:36:53 +00:00
Justin Clark-Casey (justincc) 6fa2e685a9 Add incoming packet async handling engine to queue some inbound udp async requests.
This is to reduce the potential for overload of the threadpool if there are many simultaneous requets in high concurrency situations.
Currently only applied to AvatarProperties and GenericMessage requests.
2014-11-05 19:02:26 +00:00
Justin Clark-Casey (justincc) 297b27e473 Add missing class from recent commit 69abade 2014-11-04 18:26:35 +00:00
Justin Clark-Casey (justincc) 9c8d5f54d8 Add "show threadpool calls active" console debug command.
This shows named threadpool calls (excluding timer and network calls) that are currently queued or running.
Also shows total of labelled and any anonymous calls.
2014-11-04 17:21:22 +00:00
Justin Clark-Casey (justincc) 69abadedae Add naive implementation of controlled incoming HG attachments to manage load.
Instead of processing all incoming attachment scene object concurrently, process them consecutively to eliminate potential overload from this source.
This is a naive implementation because it does not currently account for slow foreign asset services.
Although it may take longer, this approach may also improve attachment visibility for HG avatars
since the scene object is now always added to the scene after receiving assets from the foreign service and not before.
2014-11-04 16:49:32 +00:00
Justin Clark-Casey (justincc) 3372210c46 Label all threadpool calls being made in core OpenSimulator. This is to add problem diagnosis.
"show threadpool calls" now also returns named (labelled), anonymous (unlabelled) and total call stats.
2014-11-04 00:55:48 +00:00
Justin Clark-Casey (justincc) d3a550bc85 Add "show threadpool calls" command to show count of all labelled smartthreadpool calls 2014-11-03 23:49:11 +00:00
Justin Clark-Casey (justincc) 965c53ebd7 Just for now, don't alert the user or log if we couldn't change their server-side preferences due to no e-mail address being sent.
This is to avoid user confusion in the oscc rehearsal as they are often not aware that this fails because no e-mail is set.
Also may be failing in the hypergrid case, though this may also be a config issue.
This is meant as a temporary solution.
2014-10-31 23:04:18 +00:00
Justin Clark-Casey (justincc) 31b92f0217 Actually persist a changed console set agent-limit via "region set".
Unfortunately, it's not currently easy to do this with "max-agent-limit"
- this must be separately set as MaxAgents in region config if it's to persist over restarts.
2014-10-31 22:32:35 +00:00
Justin Clark-Casey (justincc) 12f50a2798 Add "region set" console command.
This current allows one to set two region parameters
agent-limit <int> will set the current root agent limit for the region, as also settable through the viewer, though some impose a max setting (e.g. 100).
max-agent-limit <int> will set the maximum allowed root agent limit.  This can also be set via the MaxAgent parameter in region config.
2014-10-31 21:44:31 +00:00
Justin Clark-Casey (justincc) fdb902d762 Add "region get" command as a synononym for "show region" console command.
This matches existing similar commands and a soon to be added "region set" command.
2014-10-31 21:12:25 +00:00
justincc a58ae73694 Update libomv to cedac55
This resolves an issue with pCampbot where some bots would occasionally connect with the same UDP source port.
This sometimes led to console messages where bots would report receiving packets multiple times that weren't marked as resends.
DLLs built under windows
2014-10-30 23:31:45 +00:00
Justin Clark-Casey (justincc) 43ac867512 Stop Mono 3.2.8 from binding a UDP socket to a port already in use.
At least on Mono 3.2.8 (but not under Windows), one can bind multiple UDP sockets to the same port by default.
Different simulators cannot demultiplex each other's messages, so a set of confusing non-obvious errors arise if this occurs.
This change prevents such multiple binding.
2014-10-30 20:54:23 +00:00
Justin Clark-Casey (justincc) 064897be85 On pCampbot, if we add the none (n) behaviour then make it actually stop any bots in motion.
Previously, adding this behaviour after physics (p) would leave the bot to drift off for ever in its last movement direction.
2014-10-29 00:29:11 +00:00
Justin Clark-Casey (justincc) 4275342515 Add "wearables check" console command
This checks that all the wearable assets and any assets for a given logged in avatar exist in the asset service
2014-10-28 23:00:49 +00:00
Justin Clark-Casey (justincc) cc23d4bac5 Add "wearables show" console command.
This shows summary wearables information (shape, hair, etc.) for all avatars in the scene or specific information about a given avatar's wearables.
Similar to the existing "attachments show" command.
2014-10-28 17:29:04 +00:00
Justin Clark-Casey (justincc) 1c12930c03 Fix recent regression where adaptive throttles stopped adjusting.
Extends regression tests to test response of adaptive throttles to ack'ed and expired packets.
2014-10-28 00:44:03 +00:00
Justin Clark-Casey (justincc) 096aecc097 Fix setting of max scene throttle so that setting it restricts the child client throttles properly.
In "show throttles", also renames 'total' column to 'actual' to reflect that it is not necessarily the throttles requested for/by the client.
Also fills out 'target' in non-adapative mode to the actual throttle requested for/by the client.
2014-10-25 02:09:37 +01:00
Justin Clark-Casey (justincc) bda36ad2e0 Add request drip rate to assertions for token bucket regression tests 2014-10-25 00:36:50 +01:00
Justin Clark-Casey (justincc) 67007c7d21 Add regression tests for token buckets on their own 2014-10-25 00:32:46 +01:00
Justin Clark-Casey (justincc) cdc3301a33 Make regression throttle tests consistently test target and max throttle settings.
As part of this also refactors code to put all throttle asserts in a single regression test method
2014-10-25 00:09:39 +01:00
Justin Clark-Casey (justincc) 27efecc82f minor: In "show client stats" command, properly handle the case where a client has made no AgentUpdate requests (as is the case with agents that have only even been child) rather than throwing an exception 2014-10-24 21:52:31 +01:00
Justin Clark-Casey (justincc) a17bbeca9d minor: Correct letter accidentally added to copyright notice on top of Scene.cs (almost five years ago!) 2014-10-22 23:51:14 +01:00
Justin Clark-Casey (justincc) 6fa8ddf0fc In Scene.AddNewAgent(), avoid a situation where an exception can result in a client being added to the manager without IClientAPI.SceneAgent being set.
This is done by adjusting the order of code so that SceneAgent will always be set before adding the client.
Various parts of the code (rightly) assume that a a client registered to the manager will always have a SceneAgent set no matter what.
2014-10-22 23:46:05 +01:00
Justin Clark-Casey (justincc) 7d2aad3873 For now, send all non-full terse updates for ones own avatar directly to the LLUDP client stack rather than queueing internally within LLClientView.
When an HG avatar enters a scene, it delays processing of entity updates.  Could be crowding out by other updates or something else.
This delay in ones own av mvmt updates results in mvmt lag experienced on the client.  Avoiding the internal LLClientView for these packets appears to resolve this issue.
Appears most noticeably for avatars with attachments, though has also been seen on those without sometimes.  Hasn't been observed for non-HG avatars in general.
Will be investigating exactly what the problem is, at which point there will be a more permanent solution.
2014-10-21 18:27:09 +01:00
Justin Clark-Casey (justincc) c1a2c4a0bd Add "debug lludp throttles get/set request" and get current
This allows one to set the requested throttle (which normally comes from the client) as opposed to the max.
2014-10-21 02:29:39 +01:00
Justin Clark-Casey (justincc) eaa048eb89 Change the word order of some debug lludp settings for readability
On server, scene-throttle-max becomes max-scene-throttle and likewise max-new-client-throttle
On clients, throttle-max becomes max
2014-10-21 02:17:38 +01:00
Justin Clark-Casey (justincc) bb3e68c069 Add "debug lludp get/set new-client-throttle-max" to allow default new client throttle to be set separately from existing clients.
"debug lludp throttles get/set throttle-max" now only gets and sets current max client throttles
2014-10-21 02:10:55 +01:00
Justin Clark-Casey (justincc) 5dc0730f06 Add "debug lludp get" command which currently just shows scene-throttle-max to mirror "debug lludp set"
Information is also available in "show server throttles" but that's more for non-debug info rather than attempting to get and set parameters on the fly for debug purposes.
2014-10-21 01:48:59 +01:00
Justin Clark-Casey (justincc) 3e25e6a170 Add some more llGiveInventory() regression tests 2014-10-20 23:57:16 +01:00
Melanie Thielker c6f39a0bfd Fix transferring inventory from prims to agent inventory 2014-10-20 23:50:17 +01:00
Justin Clark-Casey (justincc) 830735a42f When inserting missing CreatorData in the HGAssetMapper, do the rewrite on a streaming xml basis rather than loading it all into memory via XmlDocument.
This is because objects with lots of parts can have a lot of xml to load into memory, and this has been seen to have a noticeable performance impact.
Whereas streaming has been seen to reduce the impact in normal serialization.
Implmentation is messy but I couldn't see a better way of doing it when you can't assume that you know the exact structure of the input XML.
2014-10-20 23:46:34 +01:00
Justin Clark-Casey (justincc) dede17b0d2 Add regression test TestPostAssetRewrite() to check results of HGAssetMapper.Post() object asset rewriting, 2014-10-16 01:13:38 +01:00
Justin Clark-Casey (justincc) a54c784b81 If an exception makes it to the top of a JobEngine request, catch and log instead of letting it terminate the simulator... 2014-10-14 20:42:41 +01:00
Justin Clark-Casey (justincc) 493a86dfed Allow GetMesh capability to be served directly by a server like GetTexture
To do this required GetMesh to be converted to a BaseStreamHandler
Unlike GetTexture connector, no redirect URL functionality yet (this wasn't present in the first place).
2014-10-14 18:37:22 +01:00
Justin Clark-Casey (justincc) 22020927c6 Start JobEngine by default in simulator for now. 2014-10-14 17:13:53 +01:00
Justin Clark-Casey (justincc) b9b2f5686f If JobEngine is active, then use it to copy/rez attachments for an avatar entering the scene that isn't initially logging on. This will execute tasks consecutively rather than concurrently.
This has two aims
1) Reduce initial teleport failures when a foreign Hypergrid user enters a region by not holding up the teleport for attachment rez (this can be particularly costly when HG gets all assets in the object graph.
2) Reduce server load that may impact other simulator activities.
This complements existing JobEngine options that perform initial login attachment rez and appearance send in consecutive tasks.
2014-10-14 16:57:58 +01:00
Justin Clark-Casey (justincc) 275613ad83 minor: be consistent about reporting errors out of HttpServerBase 2014-10-14 00:35:15 +01:00
Justin Clark-Casey (justincc) 2935e79ca3 If the port entry in [Network] for a server is missing, then actually tell the user that's the problem rather than simply exiting silently.
Also exit with Environment.Exit(), not by aborting the thread.
2014-10-13 23:57:46 +01:00
Justin Clark-Casey (justincc) 0437d44785 Add an IConfigSource constructor to HGAssetBroker so that it can be instantiated directly for potentially handling some capabilities directly in services with HG active 2014-10-13 23:10:08 +01:00
Justin Clark-Casey (justincc) 00e31de872 Fix an issue where specifying both max client and server outgoing UDP throttles would cause client throttles to be lower than expected when total requests exceeded the scene limit.
This was because specifying a max client throttle would always request the max from the parent server throttle, no matter the actual total requests on the client throttle.
This would lead to a lower server multiplier than expected.
This change also adds a 'target' column to the "show throttles" output that shows the target rate (as set by client) if adaptive throttles is active.
This commit also re-adds the functionality lost in recent 5c1a1458 to set a max client throttle when adaptive is active.
This commit also adds TestClientThrottlePerClientAndRegionLimited and TestClientThrottleAdaptiveNoLimit regression tests
2014-10-10 23:36:50 +01:00
Justin Clark-Casey (justincc) 458540400a Use automatic properties for Parent and TotalDripRequest in TokenBucket to make code analysis easier. No functional change. 2014-10-10 18:38:54 +01:00
Justin Clark-Casey (justincc) 5c1a14587d Remove the unnecessary intermediate total token bucket.
This only had one child, which is the 'adaptive' token bucket.
So from testing and currently analysis, we can use that bucket directly which simplifies the code.
2014-10-10 00:58:16 +01:00
Justin Clark-Casey (justincc) b28b31b3ee minor: remove warnings from unused fields in LocalGridServicesConnector 2014-10-09 01:30:24 +01:00
Justin Clark-Casey (justincc) 16eb9253b0 minor: Remove unused field from EntityTransferModule 2014-10-09 01:25:11 +01:00
Justin Clark-Casey (justincc) ba0707ca1e minor: remove compiler warning from unused field in SimulatorFeaturesModule 2014-10-09 01:23:53 +01:00
Justin Clark-Casey (justincc) 3e4550adf7 minor: resolve warnings from unused fields in XBakesGet and Post handlers 2014-10-09 01:08:21 +01:00
Justin Clark-Casey (justincc) e902824da7 Remove compiler warning from UserProfilesHandlers 2014-10-09 01:03:45 +01:00
Justin Clark-Casey (justincc) 235eb92c0e minor: remove warning in BasicHttpAuthentication by commenting out unused remove_me parameter (which looks like it might still be potentially useful for logging) 2014-10-09 00:57:10 +01:00
Justin Clark-Casey (justincc) d69ab1e037 extend TestClientThrottleRegionLimited for 2 clients after testing 1. Renames to TestSingleClientThrottleRegionLimited() 2014-10-09 00:51:36 +01:00
Justin Clark-Casey (justincc) d69e3760e1 Add regression test TestClientThrottleRegionLimited() for testing simple behaviour of throttles where a region-wide total outbound limit is in place. 2014-10-08 23:54:11 +01:00
Justin Clark-Casey (justincc) a83f5f2153 factor out common throttle setting byte[] array construction in ThrottleTests. 2014-10-08 22:50:34 +01:00
Justin Clark-Casey (justincc) 3802f2da3b Add regression test TestClientThrottleLimited() for throttle behaviour when a max client total limit is enforced server-side 2014-10-08 21:30:52 +01:00
Justin Clark-Casey (justincc) 51043746f3 refactor: consistently put all test classes in the OpenSim.Tests.Common package rather than some in OpenSim.Tests.Common.Mock
the separate mock package was not useful and was just another using line to always add
2014-10-08 21:09:25 +01:00
Justin Clark-Casey (justincc) b6eaef79dd minor: disable logging in regression test TestClientThrottleSetNoLimit 2014-10-08 20:54:09 +01:00
Justin Clark-Casey (justincc) c993067204 minor: comment out unused method in regression BasicCircuitTests 2014-10-08 20:53:39 +01:00
Justin Clark-Casey (justincc) ef39fcf465 Add basic regression test ThrottleTests.TestClientThrottleSetNoLimit 2014-10-08 20:52:17 +01:00
Justin Clark-Casey (justincc) e8337d6a51 refactor: Move test clientstack setup code out of BasicCircuitTests into OpenSim.Tests.Common.ClientStackHelpers 2014-10-08 20:17:51 +01:00
Justin Clark-Casey (justincc) 90b31a2f54 Fix recent regression in "debug lludp throttles get" command that stopped it printing any information. Also fix max throttle displayed to be properly kbps 2014-10-08 00:46:24 +01:00
Justin Clark-Casey (justincc) b40220885e minor: in "show server throttles", display unset if new client throttle value is not set rather than 0 2014-10-08 00:31:35 +01:00
Justin Clark-Casey (justincc) 649891a0d8 Add throttle-max option to "debug lludp throttles set" to allow runtime setting of default and existing client throttles.
Doesn't yet adjust until clients submit new throttle settings.
2014-10-07 18:34:08 +01:00
Justin Clark-Casey (justincc) a529bc8f2a Change "debug lludp throttle *" commands to "debug lludp throttles" for consistency (and because I keep typing throttles).
This will still work with "debug lludp throttle" anyway.
2014-10-07 01:44:36 +01:00
Justin Clark-Casey (justincc) 16aca6ddbd small adjustment to commented out scene_throttle_max_bps and client_throttle_max_bps examples in OpenSimDefaults.ini
Make them actually reflect 20 mbit and 1.5 mbit respectively
2014-10-07 01:14:13 +01:00
Justin Clark-Casey (justincc) 38458d4be8 Change help text for client_throttle_max_bps in OpenSimDefaults.ini to state it is in bytes, not bits
This is the same as already done for scene_throttle_max_bps
Internally, the token buckets are in bytes and the other help text makes it clear that the number is bytes per second
(though with the wrong assumption that 1 mbit = 1024 * 1024 bits whereas 1 mbit = 1000 kbits = 1000000 bits)
2014-10-07 01:10:57 +01:00
Justin Clark-Casey (justincc) 9ee171f441 Fix console set and get of max scene rate. Was performing wrong calculation - throttle buckets are set in bytes, not bits 2014-10-07 01:08:22 +01:00
Justin Clark-Casey (justincc) 7ca4e2cb6f Eliminate 'max' throttle setting from server throttles report since this never applies. 2014-10-07 00:08:44 +01:00
Justin Clark-Casey (justincc) 523f0b8938 Allow "debug lludp throttle *" commands to work without a user name, in which case they apply to all users in the lludp server 2014-10-07 00:03:05 +01:00
Justin Clark-Casey (justincc) 4f04c0b560 minor: add apparant total to logging when client sets throttles 2014-10-06 23:39:52 +01:00
Justin Clark-Casey (justincc) b7f78bf0f7 minor: fix bug in throttle logging where arguments were mismatched 2014-10-06 23:34:27 +01:00
Justin Clark-Casey (justincc) 5cb3b87b21 Add "debug lludp set scene-throttle-max <value>" console command to allow us to potentially set the scene max throttle on the fly. 2014-10-06 23:29:41 +01:00
Justin Clark-Casey (justincc) 97e25a0f45 Move information about "server agent rate" throttles into "show server throttles" command rather than "show throttles"
THis allows us to see the rates when no client is connected to the region.
2014-10-06 22:18:54 +01:00
Justin Clark-Casey (justincc) 026df644b5 Add "show server throttles" command for showing server specific information about throttles
This is separate from the user-oriented "show throttles" command since one will often only want to know about varying client throttle settings.
Currently displays max scene throttle and adaptive throttles config if set.
2014-10-06 20:34:17 +01:00
Justin Clark-Casey (justincc) c3b1c72c33 refactor: Use simpler auto-implemented property for HttpPort in GridRegion 2014-10-03 01:22:25 +01:00
Justin Clark-Casey (justincc) 277dbb9acd minor: Remove compiler warning from GridRegion in IGridService 2014-10-03 00:43:04 +01:00
Justin Clark-Casey (justincc) 6cfebd66ec minor: remove compiler warning from OpenProfileClient 2014-10-03 00:38:48 +01:00
Justin Clark-Casey (justincc) c9d7eb30db Add OutgoingPacketsQueuedCount clientstack stat.
This is the total of queued outgoing packets across all connections, as also seen in the "show queues" command.
Gives some early indication of whether the simulator can't send all outgoing packets fast enough.
Though then one would want to check that this isn't due to a few bad client connections.
2014-10-02 23:49:37 +01:00
Justin Clark-Casey (justincc) db5de62394 minor: Remove compiler warnings from unused fields in TokenBucket 2014-10-02 23:20:39 +01:00
Justin Clark-Casey (justincc) 5c3f33bb48 refactor: Move LLUDPServer console commands into their own class. 2014-10-02 23:18:21 +01:00
Justin Clark-Casey (justincc) d50f85dff6 minor: Comment out received seed caps request logging for now 2014-10-02 22:37:45 +01:00
BlueWall b7abcd28e1 Fix key name in example Regions.ini file 2014-10-02 22:37:34 +01:00
AliciaRaven d53703362e When a prim is bought through BuySellModule, the click action is not changed back from buy object. This means that after the object is bought, it still appears as if for sale which can be confusing. This sets it back to touch, the default after sale.
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2014-10-02 22:37:25 +01:00
Justin Clark-Casey (justincc) 4e1f2ba1f4 Make "generate map" console command also trigger upload to maptiles as well as asset generation without performing tile generation twice. 2014-10-02 22:37:11 +01:00
Diva Canto 51951682e7 Fixed a hard-to-run-into bug in groups: at the time of creation of a group, the OwnerRoleID in the groups table was inconsistent with the roleID in the roles table. OpenSim core was not running into this bug, but 3rd party modules (like Wifi) were. 2014-10-02 22:36:23 +01:00
Justin Clark-Casey (justincc) c5c387e838 refactor: rename "debug lludp throttle status" to "debug lludp throttle get" to match set command 2014-09-30 18:43:02 +01:00
Justin Clark-Casey (justincc) f7fef5bc3b Add "debug lludp throttle set" command to allow setting of parameters at runtime
Can currently only set adaptive true|false, where adaptive = false
2014-09-30 18:41:04 +01:00
Justin Clark-Casey (justincc) 8e5a62c8e7 Add "debug lludp throttle status" command to return status information about a client's throttle (currently just whether adaptive is enabled). 2014-09-30 18:18:34 +01:00
Justin Clark-Casey (justincc) ad15e06611 Add "debug lludp throttle log <level> <avatar-first-name> <avatar-last-name>" to control extra throttle related debug logging. 2014-09-30 18:12:51 +01:00
Justin Clark-Casey (justincc) 319c51b8a8 Don't unnecessarily remove from backup objects that were not directly attached from the scene.
These are never in region backup in the first place since recent 11830c43
Extend regression test to check backup status.
2014-09-29 23:18:18 +01:00
Justin Clark-Casey (justincc) 11830c4363 Do not add attachments to the region scene object backup list.
Attachment persistence is not handled in this way and this just results in a load of busy work until a check in each SOG terminates a backup check for attachments anyway.
2014-09-27 01:02:27 +01:00
Justin Clark-Casey (justincc) 2eece5b009 Move expired objects cleaning trigger to the maintenance thread of a region rather than it's main scene loop.
[Startup] default setting UpdateTempCleaningEveryNFrames becomes UpdateTempCleaningEveryNSeconds.
Default becomes 180s instead of effective 182s (which would also vary with any changes in frame time or extra long frames)
2014-09-27 00:30:42 +01:00
Roger Kirkman 528a7f3352 Add missing HGInventoryService responsible for creating My Suitcase to StandaloneHypergrid.ini 2014-09-27 00:30:33 +01:00
Justin Clark-Casey (justincc) c02b33d592 Eliminate a few unnecessary calculations in the maintenance loop.
Also uses wait event instead of sleep for periodicity control.
2014-09-26 21:29:27 +01:00
Justin Clark-Casey (justincc) 07c6630c1c Fix recent minor regression where the default frame time wasn't being set if there was no startup config section.
Caused some regression tests to fail.
2014-09-26 21:22:41 +01:00
Justin Clark-Casey (justincc) cfc95afc3d If Bullet is running on its own thread, use a reset event to control timing rather than a sleep.
In theory, there should be no difference between these mechanisms.
However, on at least Mono 3.2.8 waiting via an event appears to be much more accurate.
2014-09-26 20:56:22 +01:00
Justin Clark-Casey (justincc) 85e04198fe Improve frame time stability by taking a few unnecessary repeated calculations out of the main scene loop.
Also uses a wait event to sleep rather than a Thread.Sleep to allow the loop to be interrupted in a more controlled manner when necessary.
2014-09-26 20:05:22 +01:00
Justin Clark-Casey (justincc) 1256d643be Add "debug lludp data out" console command for logging outgoing data just before it's put on the wire.
Unlike "debug lludp packet" which logs at the point where OpenSim first asks the clientstack to send a certain outgoing packet, this logs immediately before the actual send.
For low-level debugging purposes.
2014-09-24 23:42:57 +01:00
Justin Clark-Casey (justincc) f2715a5a2f Update libopenmetaverse to 0f4b361.
Primarily to get a small message logging improvement for pCampbot.
2014-09-24 23:22:05 +01:00
Justin Clark-Casey (justincc) d2b8281df9 Add "debug lludp packet" command to pCampbot.
This allows one to log the packets received by a particular bot that are not duplicates of already received packets.
Similar to the OpenSimulator command at the same name but currently any positive level logs all received packets.
No facility yet for logging outgoing packets.
For debug purposes.
2014-09-24 23:03:39 +01:00
Justin Clark-Casey (justincc) 6b05cfce25 Remove an unnecessary check at the bottom of Scene.CloseAgent()
At this point sp != null so no check required.
2014-09-24 00:45:19 +01:00
Justin Clark-Casey (justincc) 0448935b1b Set appearance refresh to false by default.
This setting was originally added some time ago to deal with issues where appearance was not received properly by all users.
However, it does not scale well with large numbers of agents.
Disabling to see if the original problem has abated or whether this will have to be tackled in another way.
2014-09-23 18:11:05 +01:00
Justin Clark-Casey (justincc) bc01c27c8a Replace two connecting bots state booleans in pCampbot with a single state machine.
Also adds "show status" command to pCampbot that currently just shows bot connecting state
2014-09-23 17:28:02 +01:00
Justin Clark-Casey (justincc) 95aade7fdb When osNpcMoveToTarget() is called for a sitting avatar then silently do nothing rather than throwing an error.
Resolves http://opensimulator.org/mantis/view.php?id=7311
2014-09-23 17:27:27 +01:00
Justin Clark-Casey (justincc) 7ff27e32bc Add cinderblocks and bobshaffer2 to contributors. 2014-09-23 17:27:22 +01:00
Justin Clark-Casey (justincc) 89c153ca7f Fix issues where setting llSetTextureAnim(FALSE... did not work properly).
I ended up amalgamating patches from http://opensimulator.org/mantis/view.php?id=7313 and http://opensimulator.org/mantis/view.php?id=7318
Thanks a lot to both bobshaffer2 and cinderblocks.
2014-09-23 17:27:18 +01:00
Justin Clark-Casey (justincc) a6c79fe956 Fix regression from recent a02dae5 where stand positions are no longer correct when a sit target is specified.
Adjusts stand position using just avatar position relative to the root prim instead.
Fixes http://opensimulator.org/mantis/view.php?id=7315 and preserves previous fix for http://opensimulator.org/mantis/view.php?id=7299
2014-09-23 17:27:13 +01:00
Justin Clark-Casey (justincc) 1e5c3f26f0 Make BulletSim thread be ThreadPriority.Highest if running
Will only effect Windows or mono with a patch such as https://gist.github.com/justincc/31e52218d098529b4696 applied
For test purposes
2014-09-16 18:32:36 +01:00
Justin Clark-Casey (justincc) e6df61fbe9 Make outboudn and packet inbox handling threads highest priority.
Will only have any affect under Windows or mono with a patch such as https://gist.github.com/justincc/31e52218d098529b4696 (not recommended) applied.
For assessment purposes.
2014-09-16 18:27:08 +01:00
Justin Clark-Casey (justincc) 5991a98d80 Set ThreadPriority on main scene thread to highest.
This will only have an effect on Windows systems or mono with the (not recommended) mono-3.2.8 debug patch https://gist.github.com/justincc/31e52218d098529b4696 applied
2014-09-16 18:17:05 +01:00
Justin Clark-Casey (justincc) c4ed67aeee Make proper fix for last commit wrt Mantis 7317 by replacing disallowed c char and not literal 'c' 2014-09-09 18:55:38 +01:00
Justin Clark-Casey (justincc) 258de1f17f For stat names containing periods, replace with '#' rather than throw exception
In relation to http://opensimulator.org/mantis/view.php?id=7317
2014-09-09 18:52:07 +01:00
Justin Clark-Casey (justincc) f8fa76c09f Add loglevel to jobengine that can be controlled via "debug jobengine loglevel <level>".
Defaults to 0
Level 1 currently does verbose logging about every queued and processed job.
2014-09-09 18:42:02 +01:00
Justin Clark-Casey (justincc) 0b7736b861 Temporarily add root agent rez attachments work to job engine if it is running rather than as a fire and forget.
Experiment to see if serializing attachment rez and send initial data jobs improves other parts of sim performance.
2014-09-09 18:26:41 +01:00
Justin Clark-Casey (justincc) a086adf427 Add experimental job engine to see if queueing some existing async work during root agent entry to a region improves perf rather than always attempting to execute everything concurrently
Job engine is controlled via "debug jobengine start|stop|status".
Can only currently be enabled and disabled dynamically at runtime.
Currently only applies to code sending initial region data (objects, other avatar data) to a client.
2014-09-09 18:14:56 +01:00
Justin Clark-Casey (justincc) e76cc35409 Merge branch 'master' into ghosts 2014-09-09 17:22:09 +01:00
Justin Clark-Casey (justincc) a129e9e351 Merge branch 'master' into ghosts 2014-09-03 00:26:53 +01:00
Justin Clark-Casey (justincc) 3c7eacf39b Fix recent regression from 473c5594 where camera started to judder on moving vehicles.
Other parts of OpenSimulator are relying on SP.Velocity == 0 for vehicles.
So add and use SP.GetWorldVelocity() instead when we need vehicle velocity, along the same lines as existing SP.GetWorldRotation()
2014-09-02 23:35:03 +01:00
Justin Clark-Casey (justincc) cd6f73392a Reinsert OpenMetaverse.dll from commit fbdf507 from an accidental replace in f1f935e
This only affected pCampbot.
2014-09-02 19:00:14 +01:00
Justin Clark-Casey (justincc) 1559a3a099 Merge branch 'master' into ghosts 2014-09-02 18:34:54 +01:00
Justin Clark-Casey (justincc) 6520065625 Merge branch 'master' into ghosts 2014-08-26 18:52:50 +01:00
Justin Clark-Casey (justincc) 693bfdc0fb Merge branch 'master' into ghosts 2014-08-26 18:26:27 +01:00
Justin Clark-Casey (justincc) f3eaa6d81e Temporary hack to disable av to av collisions in bulletsim.
Need to do this for a test.  Final implementation will be properly controlled through a property.
2014-08-26 18:17:09 +01:00
182 changed files with 5257 additions and 1356 deletions

View File

@ -72,10 +72,12 @@ what it is today.
* Allen Kerensky
* BigFootAg
* BlueWall Slade
* bobshaffer2
* brianw/Sir_Ahzz
* CharlieO
* ChrisDown
* Chris Yeoh (IBM)
* cinderblocks
* controlbreak
* coyled
* ctrlaltdavid (David Rowe)

View File

@ -150,7 +150,8 @@ namespace OpenSim.Groups
data.Data["ShowInList"] = showInList ? "1" : "0";
data.Data["AllowPublish"] = allowPublish ? "1" : "0";
data.Data["MaturePublish"] = maturePublish ? "1" : "0";
data.Data["OwnerRoleID"] = UUID.Random().ToString();
UUID roleID = UUID.Random();
data.Data["OwnerRoleID"] = roleID.ToString();
if (!m_Database.StoreGroup(data))
return UUID.Zero;
@ -159,7 +160,6 @@ namespace OpenSim.Groups
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true);
// Create Owner role
UUID roleID = UUID.Random();
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true);
// Add founder to group
@ -247,6 +247,9 @@ namespace OpenSim.Groups
if (group == null)
return members;
// Unfortunately this doesn't quite work on legacy group data because of a bug
// that's also being fixed here on CreateGroup. The OwnerRoleID sent to the DB was wrong.
// See how to find the ownerRoleID a few lines below.
UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]);
RoleData[] roles = m_Database.RetrieveRoles(GroupID);
@ -255,6 +258,11 @@ namespace OpenSim.Groups
return members;
List<RoleData> rolesList = new List<RoleData>(roles);
// Let's find the "real" ownerRoleID
RoleData ownerRole = rolesList.Find(r => r.Data["Powers"] == ((long)OwnerPowers).ToString());
if (ownerRole != null)
ownerRoleID = ownerRole.RoleID;
// Check visibility?
// When we don't want to check visibility, we pass it "all" as the requestingAgentID
bool checkVisibility = !RequestingAgentID.Equals(UUID.Zero.ToString());
@ -291,17 +299,17 @@ namespace OpenSim.Groups
{
m.Title = selected.Data["Title"];
m.AgentPowers = UInt64.Parse(selected.Data["Powers"]);
m.AgentID = d.PrincipalID;
m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false;
m.Contribution = Int32.Parse(d.Data["Contribution"]);
m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false;
// Is this person an owner of the group?
m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false;
members.Add(m);
}
m.AgentID = d.PrincipalID;
m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false;
m.Contribution = Int32.Parse(d.Data["Contribution"]);
m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false;
// Is this person an owner of the group?
m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false;
members.Add(m);
}
return members;

View File

@ -30,6 +30,7 @@ using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.IO;
using System.Text;
using System.Web;
using log4net;
using Nini.Config;
@ -43,41 +44,37 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class GetMeshHandler
public class GetMeshHandler : BaseStreamHandler
{
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IAssetService m_assetService;
public GetMeshHandler(IAssetService assService)
public GetMeshHandler(string path, IAssetService assService, string name, string description)
: base("GET", path, name, description)
{
m_assetService = assService;
}
public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
Hashtable responsedata = new Hashtable();
responsedata["int_response_code"] = 400; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Request wasn't what was expected";
// Try to parse the texture ID from the request URL
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
string meshStr = query.GetOne("mesh_id");
string meshStr = string.Empty;
if (request.ContainsKey("mesh_id"))
meshStr = request["mesh_id"].ToString();
// m_log.DebugFormat("Fetching mesh {0}", meshStr);
UUID meshID = UUID.Zero;
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
{
if (m_assetService == null)
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
return responsedata;
httpResponse.StatusCode = 404;
httpResponse.ContentType = "text/plain";
byte[] data = Encoding.UTF8.GetBytes("The asset service is unavailable. So is your mesh.");
httpResponse.Body.Write(data, 0, data.Length);
return null;
}
AssetBase mesh = m_assetService.Get(meshID.ToString());
@ -86,31 +83,32 @@ namespace OpenSim.Capabilities.Handlers
{
if (mesh.Type == (SByte)AssetType.Mesh)
{
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["content_type"] = "application/vnd.ll.mesh";
responsedata["int_response_code"] = 200;
byte[] data = mesh.Data;
httpResponse.Body.Write(data, 0, data.Length);
httpResponse.ContentType = "application/vnd.ll.mesh";
httpResponse.StatusCode = 200;
}
// Optionally add additional mesh types here
else
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
return responsedata;
httpResponse.StatusCode = 404;
httpResponse.ContentType = "text/plain";
byte[] data = Encoding.UTF8.GetBytes("Unfortunately, this asset isn't a mesh.");
httpResponse.Body.Write(data, 0, data.Length);
httpResponse.KeepAlive = false;
}
}
else
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
return responsedata;
httpResponse.StatusCode = 404;
httpResponse.ContentType = "text/plain";
byte[] data = Encoding.UTF8.GetBytes("Your Mesh wasn't found. Sorry!");
httpResponse.Body.Write(data, 0, data.Length);
httpResponse.KeepAlive = false;
}
}
return responsedata;
return null;
}
}
}

View File

@ -65,15 +65,8 @@ namespace OpenSim.Capabilities.Handlers
if (m_AssetService == null)
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
IRequestHandler reqHandler
= new RestHTTPHandler(
"GET",
"/CAPS/" + UUID.Random(),
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
"GetMesh",
null);
server.AddStreamHandler(reqHandler);
server.AddStreamHandler(
new GetMeshHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null));
}
}
}

View File

@ -37,7 +37,6 @@ using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
{

View File

@ -54,6 +54,10 @@ namespace OpenSim.Framework
public int assetThrottle;
public int textureThrottle;
public int totalThrottle;
// Used by adaptive only
public int targetThrottle;
public int maxThrottle;
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();

View File

@ -483,7 +483,7 @@ namespace OpenSim.Framework.Communications
/// In case, we are invoked asynchroneously this object will keep track of the state
/// </summary>
AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
Util.FireAndForget(RequestHelper, ar);
Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest");
return ar;
}

View File

@ -0,0 +1,320 @@
/*
* 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.Concurrent;
using System.Reflection;
using System.Threading;
using log4net;
using OpenSim.Framework;
namespace OpenSim.Framework.Monitoring
{
public class Job
{
public string Name;
public WaitCallback Callback;
public object O;
public Job(string name, WaitCallback callback, object o)
{
Name = name;
Callback = callback;
O = o;
}
}
public class JobEngine
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public int LogLevel { get; set; }
public bool IsRunning { get; private set; }
/// <summary>
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
/// </summary>
public int RequestProcessTimeoutOnStop { get; set; }
/// <summary>
/// Controls whether we need to warn in the log about exceeding the max queue size.
/// </summary>
/// <remarks>
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
/// order to avoid spamming the log with lots of warnings.
/// </remarks>
private bool m_warnOverMaxQueue = true;
private BlockingCollection<Job> m_requestQueue;
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
private Stat m_requestsWaitingStat;
private Job m_currentJob;
/// <summary>
/// Used to signal that we are ready to complete stop.
/// </summary>
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
public JobEngine()
{
RequestProcessTimeoutOnStop = 5000;
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug jobengine",
"debug jobengine <start|stop|status|log>",
"Start, stop, get status or set logging level of the job engine.",
"If stopped then all outstanding jobs are processed immediately.",
HandleControlCommand);
}
public void Start()
{
lock (this)
{
if (IsRunning)
return;
IsRunning = true;
m_finishedProcessingAfterStop.Reset();
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
m_requestsWaitingStat =
new Stat(
"JobsWaiting",
"Number of jobs waiting for processing.",
"",
"",
"server",
"jobengine",
StatType.Pull,
MeasuresOfInterest.None,
stat => stat.Value = m_requestQueue.Count,
StatVerbosity.Debug);
StatsManager.RegisterStat(m_requestsWaitingStat);
Watchdog.StartThread(
ProcessRequests,
"JobEngineThread",
ThreadPriority.Normal,
false,
true,
null,
int.MaxValue);
}
}
public void Stop()
{
lock (this)
{
try
{
if (!IsRunning)
return;
IsRunning = false;
int requestsLeft = m_requestQueue.Count;
if (requestsLeft <= 0)
{
m_cancelSource.Cancel();
}
else
{
m_log.InfoFormat("[JOB ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
while (requestsLeft > 0)
{
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
{
// After timeout no events have been written
if (requestsLeft == m_requestQueue.Count)
{
m_log.WarnFormat(
"[JOB ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
RequestProcessTimeoutOnStop, requestsLeft);
break;
}
}
requestsLeft = m_requestQueue.Count;
}
}
}
finally
{
m_cancelSource.Dispose();
StatsManager.DeregisterStat(m_requestsWaitingStat);
m_requestsWaitingStat = null;
m_requestQueue = null;
}
}
}
public bool QueueRequest(string name, WaitCallback req, object o)
{
if (LogLevel >= 1)
m_log.DebugFormat("[JOB ENGINE]: Queued job {0}", name);
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
{
// m_log.DebugFormat(
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
// categories, client.AgentID, m_udpServer.Scene.Name);
m_requestQueue.Add(new Job(name, req, o));
if (!m_warnOverMaxQueue)
m_warnOverMaxQueue = true;
return true;
}
else
{
if (m_warnOverMaxQueue)
{
// m_log.WarnFormat(
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
// client.AgentID, m_udpServer.Scene.Name);
m_log.WarnFormat("[JOB ENGINE]: Request queue at maximum capacity, not recording job");
m_warnOverMaxQueue = false;
}
return false;
}
}
private void ProcessRequests()
{
try
{
while (IsRunning || m_requestQueue.Count > 0)
{
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
// QueueEmpty callback = req.Client.OnQueueEmpty;
//
// if (callback != null)
// {
// try
// {
// callback(req.Categories);
// }
// catch (Exception e)
// {
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
// }
// }
if (LogLevel >= 1)
m_log.DebugFormat("[JOB ENGINE]: Processing job {0}", m_currentJob.Name);
try
{
m_currentJob.Callback.Invoke(m_currentJob.O);
}
catch (Exception e)
{
m_log.Error(
string.Format(
"[JOB ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
}
if (LogLevel >= 1)
m_log.DebugFormat("[JOB ENGINE]: Processed job {0}", m_currentJob.Name);
m_currentJob = null;
}
}
catch (OperationCanceledException)
{
}
m_finishedProcessingAfterStop.Set();
}
private void HandleControlCommand(string module, string[] args)
{
// if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
// return;
if (args.Length < 3)
{
MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|log>");
return;
}
string subCommand = args[2];
if (subCommand == "stop")
{
Stop();
MainConsole.Instance.OutputFormat("Stopped job engine.");
}
else if (subCommand == "start")
{
Start();
MainConsole.Instance.OutputFormat("Started job engine.");
}
else if (subCommand == "status")
{
MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
MainConsole.Instance.OutputFormat(
"Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
}
else if (subCommand == "log")
{
// int logLevel;
int logLevel = int.Parse(args[3]);
// if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
// {
LogLevel = logLevel;
MainConsole.Instance.OutputFormat("Set debug log level to {0}", LogLevel);
// }
}
else
{
MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
}
}
}
}

View File

@ -171,7 +171,8 @@ namespace OpenSim.Framework.Monitoring
foreach (char c in DisallowedShortNameCharacters)
{
if (shortName.IndexOf(c) != -1)
throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
shortName = shortName.Replace(c, '#');
// throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
}
ShortName = shortName;

View File

@ -133,6 +133,8 @@ namespace OpenSim.Framework.Monitoring
/// /summary>
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
public static JobEngine JobEngine { get; private set; }
/// <summary>
/// Is this watchdog active?
/// </summary>
@ -173,6 +175,7 @@ namespace OpenSim.Framework.Monitoring
static Watchdog()
{
JobEngine = new JobEngine();
m_threads = new Dictionary<int, ThreadWatchdogInfo>();
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
m_watchdogTimer.AutoReset = false;
@ -449,5 +452,55 @@ namespace OpenSim.Framework.Monitoring
m_watchdogTimer.Start();
}
/// <summary>
/// Run a job.
/// </summary>
/// <remarks>
/// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job
/// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is
/// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to
/// perform work at once (e.g. in conference situations). With lower numbers of connections, the small
/// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more
/// sophisticated implementation could perform jobs concurrently when the server is under low load.
///
/// However, be advised that some callers of this function rely on all jobs being performed in sequence if any
/// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine
/// beyond a single thread will require considerable thought.
///
/// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot
/// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues
/// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where
/// the job engine could be improved and so CPU utilization improved by better management of concurrency within
/// OpenSimulator.
/// </remarks>
/// <param name="jobType">General classification for the job (e.g. "RezAttachments").</param>
/// <param name="callback">Callback for job.</param>
/// <param name="name">Specific name of job (e.g. "RezAttachments for Joe Bloggs"</param>
/// <param name="obj">Object to pass to callback when run</param>
/// <param name="canRunInThisThread">If set to true then the job may be run in ths calling thread.</param>
/// <param name="mustNotTimeout">If the true then the job must never timeout.</param>
/// <param name="log">If set to true then extra logging is performed.</param>
public static void RunJob(
string jobType, WaitCallback callback, string name, object obj,
bool canRunInThisThread = false, bool mustNotTimeout = false,
bool log = false)
{
if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
{
Culture.SetCurrentCulture();
callback(obj);
return;
}
if (JobEngine.IsRunning)
JobEngine.QueueRequest(name, callback, obj);
else if (canRunInThisThread)
callback(obj);
else if (mustNotTimeout)
RunInThread(callback, name, obj, log);
else
Util.FireAndForget(callback, obj, name);
}
}
}

View File

@ -127,7 +127,6 @@ namespace OpenSim.Framework
private int m_objectCapacity = 0;
private int m_maxPrimsPerUser = -1;
private int m_linksetCapacity = 0;
private int m_agentCapacity = 0;
private string m_regionType = String.Empty;
private RegionLightShareData m_windlight = new RegionLightShareData();
protected uint m_httpPort;
@ -351,10 +350,7 @@ namespace OpenSim.Framework
get { return m_linksetCapacity; }
}
public int AgentCapacity
{
get { return m_agentCapacity; }
}
public int AgentCapacity { get; set; }
public byte AccessLevel
{
@ -748,7 +744,7 @@ namespace OpenSim.Framework
#endregion
m_agentCapacity = config.GetInt("MaxAgents", 100);
AgentCapacity = config.GetInt("MaxAgents", 100);
allKeys.Remove("MaxAgents");
// Multi-tenancy
@ -864,8 +860,8 @@ namespace OpenSim.Framework
if (m_linksetCapacity > 0)
config.Set("LinksetPrims", m_linksetCapacity);
if (m_agentCapacity > 0)
config.Set("MaxAgents", m_agentCapacity);
if (AgentCapacity > 0)
config.Set("MaxAgents", AgentCapacity);
if (ScopeID != UUID.Zero)
config.Set("ScopeID", ScopeID.ToString());

View File

@ -29,6 +29,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@ -291,6 +292,18 @@ namespace OpenSim.Framework.Servers
+ " 3 = full stack trace, including common threads\n",
HandleDebugThreadpoolLevel);
m_console.Commands.AddCommand(
"Debug", false, "show threadpool calls active",
"show threadpool calls active",
"Show details about threadpool calls that are still active (currently waiting or in progress)",
HandleShowThreadpoolCallsActive);
m_console.Commands.AddCommand(
"Debug", false, "show threadpool calls complete",
"show threadpool calls complete",
"Show details about threadpool calls that have been completed.",
HandleShowThreadpoolCallsComplete);
m_console.Commands.AddCommand(
"Debug", false, "force gc",
"force gc",
@ -354,6 +367,57 @@ namespace OpenSim.Framework.Servers
Notice("serialosdreq is now {0}", setSerializeOsdRequests);
}
private void HandleShowThreadpoolCallsActive(string module, string[] args)
{
List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsInProgress().ToList();
calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
int namedCalls = 0;
ConsoleDisplayList cdl = new ConsoleDisplayList();
foreach (KeyValuePair<string, int> kvp in calls)
{
if (kvp.Value > 0)
{
cdl.AddRow(kvp.Key, kvp.Value);
namedCalls += kvp.Value;
}
}
cdl.AddRow("TOTAL NAMED", namedCalls);
long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls;
long allRunningCalls = Util.TotalRunningFireAndForgetCalls;
cdl.AddRow("TOTAL QUEUED", allQueuedCalls);
cdl.AddRow("TOTAL RUNNING", allRunningCalls);
cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls);
cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls);
MainConsole.Instance.Output(cdl.ToString());
}
private void HandleShowThreadpoolCallsComplete(string module, string[] args)
{
List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsMade().ToList();
calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
int namedCallsMade = 0;
ConsoleDisplayList cdl = new ConsoleDisplayList();
foreach (KeyValuePair<string, int> kvp in calls)
{
cdl.AddRow(kvp.Key, kvp.Value);
namedCallsMade += kvp.Value;
}
cdl.AddRow("TOTAL NAMED", namedCallsMade);
long allCallsMade = Util.TotalFireAndForgetCallsMade;
cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade);
cdl.AddRow("TOTAL ALL", allCallsMade);
MainConsole.Instance.Output(cdl.ToString());
}
private void HandleDebugThreadpoolStatus(string module, string[] args)
{
int workerThreads, iocpThreads;

View File

@ -15,7 +15,7 @@ namespace OpenSim.Framework.ServiceAuth
private string m_Username, m_Password;
private string m_CredentialsB64;
private string remove_me;
// private string remove_me;
public string Credentials
{
@ -24,7 +24,7 @@ namespace OpenSim.Framework.ServiceAuth
public BasicHttpAuthentication(IConfigSource config, string section)
{
remove_me = section;
// remove_me = section;
m_Username = Util.GetConfigVarFromSections<string>(config, "HttpAuthUsername", new string[] { "Network", section }, string.Empty);
m_Password = Util.GetConfigVarFromSections<string>(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty);
string str = m_Username + ":" + m_Password;

View File

@ -32,7 +32,6 @@ using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using Animation = OpenSim.Framework.Animation;
namespace OpenSim.Framework.Tests

View File

@ -1928,11 +1928,6 @@ namespace OpenSim.Framework
}
}
public static void FireAndForget(System.Threading.WaitCallback callback)
{
FireAndForget(callback, null, null);
}
public static void InitThreadPool(int minThreads, int maxThreads)
{
if (maxThreads < 2)
@ -1977,8 +1972,7 @@ namespace OpenSim.Framework
throw new NotImplementedException();
}
}
/// <summary>
/// Additional information about threads in the main thread pool. Used to time how long the
/// thread has been running, and abort it if it has timed-out.
@ -2052,12 +2046,15 @@ namespace OpenSim.Framework
}
}
private static long nextThreadFuncNum = 0;
private static long numQueuedThreadFuncs = 0;
private static long numRunningThreadFuncs = 0;
private static long numTotalThreadFuncsCalled = 0;
private static Int32 threadFuncOverloadMode = 0;
public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
// Maps (ThreadFunc number -> Thread)
private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
@ -2086,14 +2083,49 @@ namespace OpenSim.Framework
}
}
public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } }
public static Dictionary<string, int> GetFireAndForgetCallsMade()
{
return new Dictionary<string, int>(m_fireAndForgetCallsMade);
}
private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>();
public static Dictionary<string, int> GetFireAndForgetCallsInProgress()
{
return new Dictionary<string, int>(m_fireAndForgetCallsInProgress);
}
private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>();
public static void FireAndForget(System.Threading.WaitCallback callback)
{
FireAndForget(callback, null, null);
}
public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
{
FireAndForget(callback, obj, null);
}
public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
{
Interlocked.Increment(ref numTotalThreadFuncsCalled);
if (context != null)
{
if (!m_fireAndForgetCallsMade.ContainsKey(context))
m_fireAndForgetCallsMade[context] = 1;
else
m_fireAndForgetCallsMade[context]++;
if (!m_fireAndForgetCallsInProgress.ContainsKey(context))
m_fireAndForgetCallsInProgress[context] = 1;
else
m_fireAndForgetCallsInProgress[context]++;
}
WaitCallback realCallback;
bool loggingEnabled = LogThreadPool > 0;
@ -2104,7 +2136,15 @@ namespace OpenSim.Framework
if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
{
// If we're running regression tests, then we want any exceptions to rise up to the test code.
realCallback = o => { Culture.SetCurrentCulture(); callback(o); };
realCallback =
o =>
{
Culture.SetCurrentCulture();
callback(o);
if (context != null)
m_fireAndForgetCallsInProgress[context]--;
};
}
else
{
@ -2143,6 +2183,9 @@ namespace OpenSim.Framework
activeThreads.TryRemove(threadFuncNum, out dummy);
if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
if (context != null)
m_fireAndForgetCallsInProgress[context]--;
}
};
}

View File

@ -337,6 +337,10 @@ namespace OpenSim
{
// Called from base.StartUp()
IConfig startupConfig = Config.Configs["Startup"];
if (startupConfig == null || startupConfig.GetBoolean("JobEngineEnabled", true))
Watchdog.JobEngine.Start();
m_httpServerPort = m_networkServersInfo.HttpListenerPort;
SceneManager.OnRestartSim += HandleRestartRegion;

View File

@ -268,8 +268,8 @@ namespace OpenSim.Region.ClientStack.Linden
public string SeedCapRequest(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
m_log.DebugFormat(
"[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
// 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))
{

View File

@ -43,7 +43,6 @@ using OpenSim.Region.CoreModules.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ClientStack.Linden.Tests
{

View File

@ -110,32 +110,29 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion
public void RegisterCaps(UUID agentID, Caps caps)
{
// UUID capID = UUID.Random();
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
if (m_URL == "localhost")
{
// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
IRequestHandler reqHandler
= new RestHTTPHandler(
"GET",
"/CAPS/" + UUID.Random(),
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
"GetMesh",
agentID.ToString());
// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
caps.RegisterHandler("GetMesh", reqHandler);
UUID capID = UUID.Random();
caps.RegisterHandler(
"GetMesh",
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString()));
}
else
{
// m_log.DebugFormat("[GETMESH]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
caps.RegisterHandler("GetMesh", m_URL);
// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>();
if (handler != null)
handler.RegisterExternalUserCapsHandler(agentID, caps, "GetMesh", m_URL);
else
caps.RegisterHandler("GetMesh", m_URL);
}
}
}
}
}
}

View File

@ -57,8 +57,8 @@ namespace OpenSim.Region.ClientStack.Linden
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimulatorFeaturesModule")]
public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public event SimulatorFeaturesRequestDelegate OnSimulatorFeaturesRequest;

View File

@ -47,7 +47,6 @@ using OpenSim.Region.CoreModules.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;

View File

@ -0,0 +1,328 @@
/*
* 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.Concurrent;
using System.Reflection;
using System.Threading;
using log4net;
using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public class Job
{
public string Name;
public WaitCallback Callback;
public object O;
public Job(string name, WaitCallback callback, object o)
{
Name = name;
Callback = callback;
O = o;
}
}
// TODO: These kinds of classes MUST be generalized with JobEngine, etc.
public class IncomingPacketAsyncHandlingEngine
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public int LogLevel { get; set; }
public bool IsRunning { get; private set; }
/// <summary>
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
/// </summary>
public int RequestProcessTimeoutOnStop { get; set; }
/// <summary>
/// Controls whether we need to warn in the log about exceeding the max queue size.
/// </summary>
/// <remarks>
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
/// order to avoid spamming the log with lots of warnings.
/// </remarks>
private bool m_warnOverMaxQueue = true;
private BlockingCollection<Job> m_requestQueue;
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
private LLUDPServer m_udpServer;
private Stat m_requestsWaitingStat;
private Job m_currentJob;
/// <summary>
/// Used to signal that we are ready to complete stop.
/// </summary>
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
public IncomingPacketAsyncHandlingEngine(LLUDPServer server)
{
//LogLevel = 1;
m_udpServer = server;
RequestProcessTimeoutOnStop = 5000;
// MainConsole.Instance.Commands.AddCommand(
// "Debug",
// false,
// "debug jobengine",
// "debug jobengine <start|stop|status>",
// "Start, stop or get status of the job engine.",
// "If stopped then all jobs are processed immediately.",
// HandleControlCommand);
}
public void Start()
{
lock (this)
{
if (IsRunning)
return;
IsRunning = true;
m_finishedProcessingAfterStop.Reset();
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
m_requestsWaitingStat =
new Stat(
"IncomingPacketAsyncRequestsWaiting",
"Number of incoming packets waiting for async processing in engine.",
"",
"",
"clientstack",
m_udpServer.Scene.Name,
StatType.Pull,
MeasuresOfInterest.None,
stat => stat.Value = m_requestQueue.Count,
StatVerbosity.Debug);
StatsManager.RegisterStat(m_requestsWaitingStat);
Watchdog.StartThread(
ProcessRequests,
string.Format("Incoming Packet Async Handling Engine Thread ({0})", m_udpServer.Scene.Name),
ThreadPriority.Normal,
false,
true,
null,
int.MaxValue);
}
}
public void Stop()
{
lock (this)
{
try
{
if (!IsRunning)
return;
IsRunning = false;
int requestsLeft = m_requestQueue.Count;
if (requestsLeft <= 0)
{
m_cancelSource.Cancel();
}
else
{
m_log.InfoFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
while (requestsLeft > 0)
{
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
{
// After timeout no events have been written
if (requestsLeft == m_requestQueue.Count)
{
m_log.WarnFormat(
"[INCOMING PACKET ASYNC HANDLING ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
RequestProcessTimeoutOnStop, requestsLeft);
break;
}
}
requestsLeft = m_requestQueue.Count;
}
}
}
finally
{
m_cancelSource.Dispose();
StatsManager.DeregisterStat(m_requestsWaitingStat);
m_requestsWaitingStat = null;
m_requestQueue = null;
}
}
}
public bool QueueRequest(string name, WaitCallback req, object o)
{
if (LogLevel >= 1)
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Queued job {0}", name);
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
{
// m_log.DebugFormat(
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
// categories, client.AgentID, m_udpServer.Scene.Name);
m_requestQueue.Add(new Job(name, req, o));
if (!m_warnOverMaxQueue)
m_warnOverMaxQueue = true;
return true;
}
else
{
if (m_warnOverMaxQueue)
{
// m_log.WarnFormat(
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
// client.AgentID, m_udpServer.Scene.Name);
m_log.WarnFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Request queue at maximum capacity, not recording job");
m_warnOverMaxQueue = false;
}
return false;
}
}
private void ProcessRequests()
{
try
{
while (IsRunning || m_requestQueue.Count > 0)
{
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
// QueueEmpty callback = req.Client.OnQueueEmpty;
//
// if (callback != null)
// {
// try
// {
// callback(req.Categories);
// }
// catch (Exception e)
// {
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
// }
// }
if (LogLevel >= 1)
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processing job {0}", m_currentJob.Name);
try
{
m_currentJob.Callback.Invoke(m_currentJob.O);
}
catch (Exception e)
{
m_log.Error(
string.Format(
"[INCOMING PACKET ASYNC HANDLING ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
}
if (LogLevel >= 1)
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processed job {0}", m_currentJob.Name);
m_currentJob = null;
}
}
catch (OperationCanceledException)
{
}
m_finishedProcessingAfterStop.Set();
}
// private void HandleControlCommand(string module, string[] args)
// {
// // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
// // return;
//
// if (args.Length < 3)
// {
// MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|loglevel>");
// return;
// }
//
// string subCommand = args[2];
//
// if (subCommand == "stop")
// {
// Stop();
// MainConsole.Instance.OutputFormat("Stopped job engine.");
// }
// else if (subCommand == "start")
// {
// Start();
// MainConsole.Instance.OutputFormat("Started job engine.");
// }
// else if (subCommand == "status")
// {
// MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
// MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
// MainConsole.Instance.OutputFormat(
// "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
// MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
// }
//
// else if (subCommand == "loglevel")
// {
// // int logLevel;
// int logLevel = int.Parse(args[3]);
// // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
// // {
// LogLevel = logLevel;
// MainConsole.Instance.OutputFormat("Set log level to {0}", LogLevel);
// // }
// }
// else
// {
// MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
// }
// }
}
}

View File

@ -647,13 +647,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </param>
/// <returns>true if the handler was added. This is currently always the case.</returns>
public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
{
return AddLocalPacketHandler(packetType, handler, doAsync, false);
}
/// <summary>
/// Add a handler for the given packet type.
/// </summary>
/// <param name="packetType"></param>
/// <param name="handler"></param>
/// <param name="doAsync">
/// If true, when the packet is received handle it on a different thread. Whether this is given direct to
/// a threadpool thread or placed in a queue depends on the inEngine parameter.
/// </param>
/// <param name="inEngine">
/// If async is false then this parameter is ignored.
/// If async is true and inEngine is false, then the packet is sent directly to a
/// threadpool thread.
/// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine.
/// This may result in slower handling but reduces the risk of overloading the simulator when there are many
/// simultaneous async requests.
/// </param>
/// <returns>true if the handler was added. This is currently always the case.</returns>
public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine)
{
bool result = false;
lock (m_packetHandlers)
{
if (!m_packetHandlers.ContainsKey(packetType))
{
m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync });
m_packetHandlers.Add(
packetType, new PacketProcessor() { method = handler, Async = doAsync, InEngine = inEngine });
result = true;
}
}
@ -688,21 +712,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
PacketProcessor pprocessor;
if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor))
{
ClientInfo cinfo = UDPClient.GetClientInfo();
//there is a local handler for this packet type
if (pprocessor.Async)
{
ClientInfo cinfo = UDPClient.GetClientInfo();
if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
cinfo.AsyncRequests[packet.Type.ToString()] = 0;
cinfo.AsyncRequests[packet.Type.ToString()]++;
object obj = new AsyncPacketProcess(this, pprocessor.method, packet);
Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString());
if (pprocessor.InEngine)
m_udpServer.IpahEngine.QueueRequest(
packet.Type.ToString(),
ProcessSpecificPacketAsync,
obj);
else
Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString());
result = true;
}
else
{
ClientInfo cinfo = UDPClient.GetClientInfo();
if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
cinfo.SyncRequests[packet.Type.ToString()] = 0;
cinfo.SyncRequests[packet.Type.ToString()]++;
@ -1161,7 +1193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="map">heightmap</param>
public virtual void SendLayerData(float[] map)
{
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData(), "LLClientView.DoSendLayerData");
}
/// <summary>
@ -1373,7 +1405,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="windSpeeds">16x16 array of wind speeds</param>
public virtual void SendWindData(Vector2[] windSpeeds)
{
Util.FireAndForget(DoSendWindData, windSpeeds);
Util.FireAndForget(DoSendWindData, windSpeeds, "LLClientView.SendWindData");
}
/// <summary>
@ -1382,7 +1414,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="windSpeeds">16x16 array of cloud densities</param>
public virtual void SendCloudData(float[] cloudDensity)
{
Util.FireAndForget(DoSendCloudData, cloudDensity);
Util.FireAndForget(DoSendCloudData, cloudDensity, "LLClientView.SendCloudData");
}
/// <summary>
@ -3834,11 +3866,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
{
//double priority = m_prioritizer.GetUpdatePriority(this, entity);
uint priority = m_prioritizer.GetUpdatePriority(this, entity);
if (entity.UUID == m_agentId && !updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
{
ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
lock (m_entityUpdates.SyncRoot)
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = Utils.FloatToUInt16(1, 0.0f, 1.0f);
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
packet.ObjectData[0] = CreateImprovedTerseBlock(entity, false);
OutPacket(packet, ThrottleOutPacketType.Unknown, true);
}
else
{
//double priority = m_prioritizer.GetUpdatePriority(this, entity);
uint priority = m_prioritizer.GetUpdatePriority(this, entity);
lock (m_entityUpdates.SyncRoot)
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
}
}
/// <summary>
@ -5540,10 +5586,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage);
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest);
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage, true, true);
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest, true, true);
AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate);
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate, true, true);
AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply);
AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage);
AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship);
@ -5728,8 +5774,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete);
AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete);
AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate);
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate);
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate);
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate, true, true);
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate, true, true);
AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights);
AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery);
AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry);
@ -8079,7 +8125,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
// This requests the asset if needed
HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer);
});
}, null, "LLClientView.HandleTransferRequest");
return true;
}
}
@ -12786,8 +12833,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public struct PacketProcessor
{
public PacketMethod method;
public bool Async;
/// <summary>
/// Packet handling method.
/// </summary>
public PacketMethod method { get; set; }
/// <summary>
/// Should this packet be handled asynchronously?
/// </summary>
public bool Async { get; set; }
/// <summary>
/// If async is true, should this packet be handled in the async engine or given directly to a threadpool
/// thread?
/// </summary>
public bool InEngine { get; set; }
}
public class AsyncPacketProcess

View File

@ -76,6 +76,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// or removed, this number must also change</summary>
const int THROTTLE_CATEGORY_COUNT = 8;
/// <summary>
/// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes.
/// </summary>
/// <remarks>Any level above 0 will turn on logging.</remarks>
public int DebugDataOutLevel { get; set; }
/// <summary>
/// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes.
/// </summary>
/// <remarks>Any level above 0 will turn on logging.</remarks>
public int ThrottleDebugLevel
{
get
{
return m_throttleDebugLevel;
}
set
{
m_throttleDebugLevel = value;
m_throttleClient.DebugLevel = m_throttleDebugLevel;
foreach (TokenBucket tb in m_throttleCategories)
tb.DebugLevel = m_throttleDebugLevel;
}
}
private int m_throttleDebugLevel;
/// <summary>Fired when updated networking stats are produced for this client</summary>
public event PacketStats OnPacketStats;
/// <summary>Fired when the queue for a packet category is empty. This event can be
@ -144,8 +171,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
get { return m_throttleClient; }
}
/// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket m_throttleCategory;
/// <summary>Throttle buckets for each packet category</summary>
private readonly TokenBucket[] m_throttleCategories;
/// <summary>Outgoing queues for throttled packets</summary>
@ -201,9 +226,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_maxRTO = maxRTO;
// Create a token bucket throttle for this client that has the scene token bucket as a parent
m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
// Create a token bucket throttle for the total category with the client bucket as a throttle
m_throttleCategory = new TokenBucket(m_throttleClient, 0);
m_throttleClient
= new AdaptiveTokenBucket(
string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled);
// Create an array of token buckets for this clients different throttle categories
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@ -215,8 +242,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Initialize the packet outboxes, where packets sit while they are waiting for tokens
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
// Initialize the token buckets that control the throttling for each category
m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
m_throttleCategories[i]
= new TokenBucket(
string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name),
m_throttleClient, rates.GetRate(type), 0);
}
// Default the retransmission timeout to one second
@ -261,7 +292,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
m_info.totalThrottle = (int)m_throttleCategory.DripRate;
m_info.totalThrottle = (int)m_throttleClient.DripRate;
m_info.targetThrottle = (int)m_throttleClient.TargetDripRate;
m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
return m_info;
@ -279,6 +311,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
throw new NotImplementedException();
}
/// <summary>
/// Get the total number of pakcets queued for this client.
/// </summary>
/// <returns></returns>
public int GetTotalPacketsQueuedCount()
{
int total = 0;
for (int i = 0; i <= (int)ThrottleOutPacketType.Asset; i++)
total += m_packetOutboxes[i].Count;
return total;
}
/// <summary>
/// Get the number of packets queued for the given throttle type.
/// </summary>
/// <returns></returns>
/// <param name="throttleType"></param>
public int GetPacketsQueuedCount(ThrottleOutPacketType throttleType)
{
if ((int)throttleType > 0)
return m_packetOutboxes[(int)throttleType].Count;
else
return 0;
}
/// <summary>
/// Return statistics information about client packet queues.
/// </summary>
@ -348,6 +407,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
if (ThrottleDebugLevel > 0)
{
long total = resend + land + wind + cloud + task + texture + asset;
m_log.DebugFormat(
"[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
}
// Make sure none of the throttles are set below our packet MTU,
// otherwise a throttle could become permanently clogged
resend = Math.Max(resend, LLUDPServer.MTU);
@ -365,10 +432,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
texture = (int)((1 - m_cannibalrate) * texture);
//int total = resend + land + wind + cloud + task + texture + asset;
//m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}",
// AgentID, resend, land, wind, cloud, task, texture, asset, total);
if (ThrottleDebugLevel > 0)
{
long total = resend + land + wind + cloud + task + texture + asset;
m_log.DebugFormat(
"[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
}
// Update the token buckets with new throttle values
if (m_throttleClient.AdaptiveEnabled)
{
long total = resend + land + wind + cloud + task + texture + asset;
m_throttleClient.TargetDripRate = total;
}
TokenBucket bucket;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
@ -653,7 +732,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (!m_udpServer.OqrEngine.IsRunning)
{
// Asynchronously run the callback
Util.FireAndForget(FireQueueEmpty, categories);
Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty");
}
else
{

View File

@ -34,7 +34,6 @@ using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using log4net;
using NDesk.Options;
using Nini.Config;
using OpenMetaverse.Packets;
using OpenSim.Framework;
@ -222,6 +221,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
public int DefaultClientPacketDebugLevel { get; set; }
/// <summary>
/// If set then all inbound agent updates are discarded. For debugging purposes.
/// discard agent update.
/// </summary>
public bool DiscardInboundAgentUpdates { get; set; }
/// <summary>The measured resolution of Environment.TickCount</summary>
public readonly float TickCountResolution;
@ -238,12 +243,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <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>
protected TokenBucket m_throttle;
public TokenBucket Throttle { get; private set; }
/// <summary>Bandwidth throttle rates for this UDP server</summary>
/// <summary>Per client throttle rates enforced by this server</summary>
/// <remarks>
/// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
/// The other rates (resend, asset, etc.) are the defaults for a new client and can be changed (and usually
/// do get changed immediately). They do not need to sum to the total.
/// </remarks>
public ThrottleRates ThrottleRates { get; private set; }
/// <summary>Manages authentication for agent circuits</summary>
@ -355,6 +363,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
private IClientAPI m_currentIncomingClient;
/// <summary>
/// Queue some low priority but potentially high volume async requests so that they don't overwhelm available
/// threadpool threads.
/// </summary>
public IncomingPacketAsyncHandlingEngine IpahEngine { get; private set; }
/// <summary>
/// Experimental facility to run queue empty processing within a controlled number of threads rather than
/// requiring massive numbers of short-lived threads from the threadpool when there are a high number of
@ -434,12 +448,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
#endregion BinaryStats
m_throttle = new TokenBucket(null, sceneThrottleBps);
// FIXME: Can't add info here because don't know scene yet.
// m_throttle
// = new TokenBucket(
// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps);
Throttle = new TokenBucket("server throttle bucket", null, 0, sceneThrottleBps);
ThrottleRates = new ThrottleRates(configSource);
if (usePools)
EnablePools();
IpahEngine = new IncomingPacketAsyncHandlingEngine(this);
OqrEngine = new OutgoingQueueRefillEngine(this);
}
@ -447,12 +468,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
StartInbound();
StartOutbound();
IpahEngine.Start();
OqrEngine.Start();
m_elapsedMSSinceLastStatReport = Environment.TickCount;
}
private void StartInbound()
public void StartInbound()
{
m_log.InfoFormat(
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
@ -471,7 +493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
private new void StartOutbound()
public override void StartOutbound()
{
m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
@ -492,10 +514,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name);
base.StopOutbound();
base.StopInbound();
IpahEngine.Stop();
OqrEngine.Stop();
}
protected override bool EnablePools()
public override bool EnablePools()
{
if (!UsePools)
{
@ -509,7 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return false;
}
protected override bool DisablePools()
public override bool DisablePools()
{
if (UsePools)
{
@ -529,7 +552,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
/// stats.
/// </summary>
private void EnablePoolStats()
protected internal void EnablePoolStats()
{
m_poolCountStat
= new Stat(
@ -563,7 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>
/// Disables pool stats.
/// </summary>
private void DisablePoolStats()
protected internal void DisablePoolStats()
{
StatsManager.DeregisterStat(m_poolCountStat);
m_poolCountStat = null;
@ -677,312 +700,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
StatType.Pull,
stat => stat.Value = PacketPool.Instance.BlocksPooled,
StatVerbosity.Debug));
StatsManager.RegisterStat(
new Stat(
"OutgoingPacketsQueuedCount",
"Packets queued for outgoing send",
"Number of queued outgoing packets across all connections",
"",
"clientstack",
Scene.Name,
StatType.Pull,
MeasuresOfInterest.AverageChangeOverTime,
stat => stat.Value = GetTotalQueuedOutgoingPackets(),
StatVerbosity.Info));
// We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
// scene name
if (UsePools)
EnablePoolStats();
MainConsole.Instance.Commands.AddCommand(
"Debug", false, "debug lludp packet",
"debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
"Turn on packet debugging",
"If level > 255 then all incoming and outgoing packets are logged.\n"
+ "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
+ "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
+ "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
+ "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
+ "If level <= 0 then no packets are logged.\n"
+ "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
+ "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
+ "In these cases, you cannot also specify an avatar name.\n"
+ "If an avatar name is given then only packets from that avatar are logged.",
HandlePacketCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug", false, "debug lludp drop",
"debug lludp drop <in|out> <add|remove> <packet-name>",
"Drop all in or outbound packets that match the given name",
"For test purposes.",
HandleDropCommand);
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 pool",
"debug lludp pool <on|off>",
"Turn object pooling within the lludp component on or off.",
HandlePoolCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp status",
"debug lludp status",
"Return status of LLUDP packet processing.",
HandleStatusCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp toggle agentupdate",
"debug lludp toggle agentupdate",
"Toggle whether agentupdate packets are processed or simply discarded.",
HandleAgentUpdateCommand);
}
private void HandlePacketCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
bool setAsDefaultLevel = false;
bool setAll = false;
OptionSet optionSet = new OptionSet()
.Add("default", o => setAsDefaultLevel = (o != null))
.Add("all", o => setAll = (o != null));
List<string> filteredArgs = optionSet.Parse(args);
string name = null;
if (filteredArgs.Count == 6)
{
if (!(setAsDefaultLevel || setAll))
{
name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
}
else
{
MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
return;
}
}
if (filteredArgs.Count > 3)
{
int newDebug;
if (int.TryParse(filteredArgs[3], out newDebug))
{
if (setAsDefaultLevel || setAll)
{
DefaultClientPacketDebugLevel = newDebug;
MainConsole.Instance.OutputFormat(
"Packet debug for {0} clients set to {1} in {2}",
(setAll ? "all" : "future"), DefaultClientPacketDebugLevel, Scene.Name);
if (setAll)
{
Scene.ForEachScenePresence(sp =>
{
MainConsole.Instance.OutputFormat(
"Packet debug for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
sp.ControllingClient.DebugPacketLevel = newDebug;
});
}
}
else
{
Scene.ForEachScenePresence(sp =>
{
if (name == null || sp.Name == name)
{
MainConsole.Instance.OutputFormat(
"Packet debug for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
sp.ControllingClient.DebugPacketLevel = newDebug;
}
});
}
}
else
{
MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
}
}
}
private void HandleDropCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
if (args.Length != 6)
{
MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
return;
}
string direction = args[3];
string subCommand = args[4];
string packetName = args[5];
if (subCommand == "add")
{
MainConsole.Instance.OutputFormat(
"Adding packet {0} to {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
Scene.ForEachScenePresence(
sp =>
{
LLClientView llcv = (LLClientView)sp.ControllingClient;
if (direction == "in")
llcv.AddInPacketToDropSet(packetName);
else if (direction == "out")
llcv.AddOutPacketToDropSet(packetName);
}
);
}
else if (subCommand == "remove")
{
MainConsole.Instance.OutputFormat(
"Removing packet {0} from {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
Scene.ForEachScenePresence(
sp =>
{
LLClientView llcv = (LLClientView)sp.ControllingClient;
if (direction == "in")
llcv.RemoveInPacketFromDropSet(packetName);
else if (direction == "out")
llcv.RemoveOutPacketFromDropSet(packetName);
}
);
}
}
private void HandleStartCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
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 (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
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 HandlePoolCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
return;
}
string enabled = args[3];
if (enabled == "on")
{
if (EnablePools())
{
EnablePoolStats();
MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", Scene.Name);
}
}
else if (enabled == "off")
{
if (DisablePools())
{
DisablePoolStats();
MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", Scene.Name);
}
}
else
{
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
}
}
bool m_discardAgentUpdates;
private void HandleAgentUpdateCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
m_discardAgentUpdates = !m_discardAgentUpdates;
MainConsole.Instance.OutputFormat(
"Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, Scene.Name);
}
private void HandleStatusCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
return;
MainConsole.Instance.OutputFormat(
"IN LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningInbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat(
"OUT LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningOutbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", Scene.Name, UsePools ? "on" : "off");
MainConsole.Instance.OutputFormat(
"Packet debug level for new clients is {0}", DefaultClientPacketDebugLevel);
LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this);
commands.Register();
}
public bool HandlesRegion(Location x)
@ -990,6 +728,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return x == m_location;
}
public int GetTotalQueuedOutgoingPackets()
{
int total = 0;
foreach (ScenePresence sp in Scene.GetScenePresences())
{
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
total += udpClient.GetTotalPacketsQueuedCount();
}
return total;
}
// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
// {
// // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
@ -1156,6 +907,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// packet so that it isn't sent before a queued update packet.
bool forceQueue = (type == PacketType.KillObject);
// if (type == PacketType.ImprovedTerseObjectUpdate)
// {
// m_log.DebugFormat("Direct send ITOU to {0} in {1}", udpClient.AgentID, Scene.Name);
// SendPacketFinal(outgoingPacket);
// return false;
// }
// else
// {
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue))
{
SendPacketFinal(outgoingPacket);
@ -1165,6 +924,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
return false;
}
// }
#endregion Queue or Send
}
@ -1240,7 +1000,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Fire this out on a different thread so that we don't hold up outgoing packet processing for
// everybody else if this is being called due to an ack timeout.
// This is the same as processing as the async process of a logout request.
Util.FireAndForget(o => DeactivateClientDueToTimeout(client, timeoutTicks));
Util.FireAndForget(
o => DeactivateClientDueToTimeout(client, timeoutTicks), null, "LLUDPServer.DeactivateClientDueToTimeout");
return;
}
@ -1360,6 +1121,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// is 100% correct
PacketsSentCount++;
if (udpClient.DebugDataOutLevel > 0)
m_log.DebugFormat(
"[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}",
outgoingPacket.SequenceNumber, isReliable, isResend, udpClient.AgentID, Scene.Name);
// Put the UDP payload on the wire
AsyncBeginSend(buffer);
@ -1469,7 +1235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleUseCircuitCode, array);
Util.FireAndForget(HandleUseCircuitCode, array, "LLUDPServer.HandleUseCircuitCode");
return;
}
@ -1482,7 +1248,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
Util.FireAndForget(
HandleCompleteMovementIntoRegion, array, "LLUDPServer.HandleCompleteMovementIntoRegion");
return;
}
@ -1604,7 +1371,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type == PacketType.AgentUpdate)
{
if (m_discardAgentUpdates)
if (DiscardInboundAgentUpdates)
return;
((LLClientView)client).TotalAgentUpdates++;
@ -2012,7 +1779,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (!Scene.TryGetClient(agentID, out client))
{
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, Throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
client.OnLogout += LogoutHandler;
@ -2055,6 +1822,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void IncomingPacketHandler()
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
// Set this culture for the thread that incoming packets are received
// on to en-US to avoid number parsing issues
Culture.SetCurrentCulture();
@ -2100,6 +1869,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void OutgoingPacketHandler()
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
// Set this culture for the thread that outgoing packets are sent
// on to en-US to avoid number parsing issues
Culture.SetCurrentCulture();

View File

@ -0,0 +1,762 @@
/*
* 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.Text;
using NDesk.Options;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public class LLUDPServerCommands
{
private ICommandConsole m_console;
private LLUDPServer m_udpServer;
public LLUDPServerCommands(ICommandConsole console, LLUDPServer udpServer)
{
m_console = console;
m_udpServer = udpServer;
}
public void Register()
{
m_console.Commands.AddCommand(
"Comms", false, "show server throttles",
"show server throttles",
"Show information about server throttles",
HandleShowServerThrottlesCommand);
m_console.Commands.AddCommand(
"Debug", false, "debug lludp packet",
"debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
"Turn on packet debugging. This logs information when the client stack hands a processed packet off to downstream code or when upstream code first requests that a certain packet be sent.",
"If level > 255 then all incoming and outgoing packets are logged.\n"
+ "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
+ "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
+ "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
+ "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
+ "If level <= 0 then no packets are logged.\n"
+ "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
+ "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
+ "In these cases, you cannot also specify an avatar name.\n"
+ "If an avatar name is given then only packets from that avatar are logged.",
HandlePacketCommand);
m_console.Commands.AddCommand(
"Debug", false, "debug lludp data out",
"debug lludp data out <level> <avatar-first-name> <avatar-last-name>\"",
"Turn on debugging for final outgoing data to the given user's client.",
"This operates at a much lower level than the packet command and prints out available details when the data is actually sent.\n"
+ "If level > 0 then information about all outgoing UDP data for this avatar is logged.\n"
+ "If level <= 0 then no information about outgoing UDP data for this avatar is logged.",
HandleDataCommand);
m_console.Commands.AddCommand(
"Debug", false, "debug lludp drop",
"debug lludp drop <in|out> <add|remove> <packet-name>",
"Drop all in or outbound packets that match the given name",
"For test purposes.",
HandleDropCommand);
m_console.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);
m_console.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);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp pool",
"debug lludp pool <on|off>",
"Turn object pooling within the lludp component on or off.",
HandlePoolCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp status",
"debug lludp status",
"Return status of LLUDP packet processing.",
HandleStatusCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp throttles log",
"debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]",
"Change debug logging level for throttles.",
"If level >= 0 then throttle debug logging is performed.\n"
+ "If level <= 0 then no throttle debug logging is performed.",
HandleThrottleCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp throttles get",
"debug lludp throttles get [<avatar-first-name> <avatar-last-name>]",
"Return debug settings for throttles.",
"adaptive - true/false, controls adaptive throttle setting.\n"
+ "request - request drip rate in kbps.\n"
+ "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp get new-client-throttle-max' to see the setting for new clients.\n",
HandleThrottleGetCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp throttles set",
"debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]",
"Set a throttle parameter for the given client.",
"adaptive - true/false, controls adaptive throttle setting.\n"
+ "current - current drip rate in kbps.\n"
+ "request - requested drip rate in kbps.\n"
+ "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp set new-client-throttle-max' to change the settings for new clients.\n",
HandleThrottleSetCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp get",
"debug lludp get",
"Get debug parameters for the server.",
"max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
+ "max-new-client-throttle - the max kbps throttle allowed to new clients. Use 'debug lludp throttles get max' to see the settings for existing clients.",
HandleGetCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp set",
"debug lludp set <param> <value>",
"Set a parameter for the server.",
"max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
+ "max-new-client-throttle - the max kbps throttle allowed to each new client. Use 'debug lludp throttles set max' to set for existing clients.",
HandleSetCommand);
m_console.Commands.AddCommand(
"Debug",
false,
"debug lludp toggle agentupdate",
"debug lludp toggle agentupdate",
"Toggle whether agentupdate packets are processed or simply discarded.",
HandleAgentUpdateCommand);
}
private void HandleShowServerThrottlesCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
m_console.OutputFormat("Throttles for {0}", m_udpServer.Scene.Name);
ConsoleDisplayList cdl = new ConsoleDisplayList();
cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled);
long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
cdl.AddRow(
"Max scene throttle",
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
int maxClientDripRate = m_udpServer.ThrottleRates.Total;
cdl.AddRow(
"Max new client throttle",
maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
m_console.Output(cdl.ToString());
m_console.OutputFormat("{0}\n", GetServerThrottlesReport(m_udpServer));
}
private string GetServerThrottlesReport(LLUDPServer udpServer)
{
StringBuilder report = new StringBuilder();
report.AppendFormat(
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
"Total",
"Resend",
"Land",
"Wind",
"Cloud",
"Task",
"Texture",
"Asset");
report.AppendFormat(
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
"kb/s",
"kb/s",
"kb/s",
"kb/s",
"kb/s",
"kb/s",
"kb/s",
"kb/s");
ThrottleRates throttleRates = udpServer.ThrottleRates;
report.AppendFormat(
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
(throttleRates.Total * 8) / 1000,
(throttleRates.Resend * 8) / 1000,
(throttleRates.Land * 8) / 1000,
(throttleRates.Wind * 8) / 1000,
(throttleRates.Cloud * 8) / 1000,
(throttleRates.Task * 8) / 1000,
(throttleRates.Texture * 8) / 1000,
(throttleRates.Asset * 8) / 1000);
return report.ToString();
}
protected string GetColumnEntry(string entry, int maxLength, int columnPadding)
{
return string.Format(
"{0,-" + maxLength + "}{1,-" + columnPadding + "}",
entry.Length > maxLength ? entry.Substring(0, maxLength) : entry,
"");
}
private void HandleDataCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 7)
{
MainConsole.Instance.OutputFormat("Usage: debug lludp data out <true|false> <avatar-first-name> <avatar-last-name>");
return;
}
int level;
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
return;
string firstName = args[5];
string lastName = args[6];
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (sp.Firstname == firstName && sp.Lastname == lastName)
{
MainConsole.Instance.OutputFormat(
"Data debug for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
((LLClientView)sp.ControllingClient).UDPClient.DebugDataOutLevel = level;
}
});
}
private void HandleThrottleCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
bool all = args.Length == 5;
bool one = args.Length == 7;
if (!all && !one)
{
MainConsole.Instance.OutputFormat(
"Usage: debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]");
return;
}
int level;
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
return;
string firstName = null;
string lastName = null;
if (one)
{
firstName = args[5];
lastName = args[6];
}
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
{
MainConsole.Instance.OutputFormat(
"Throttle log level for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
((LLClientView)sp.ControllingClient).UDPClient.ThrottleDebugLevel = level;
}
});
}
private void HandleThrottleSetCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
bool all = args.Length == 6;
bool one = args.Length == 8;
if (!all && !one)
{
MainConsole.Instance.OutputFormat(
"Usage: debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]");
return;
}
string param = args[4];
string rawValue = args[5];
string firstName = null;
string lastName = null;
if (one)
{
firstName = args[6];
lastName = args[7];
}
if (param == "adaptive")
{
bool newValue;
if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newValue))
return;
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
{
MainConsole.Instance.OutputFormat(
"Setting param {0} to {1} for {2} ({3}) in {4}",
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
udpClient.FlowThrottle.AdaptiveEnabled = newValue;
// udpClient.FlowThrottle.MaxDripRate = 0;
// udpClient.FlowThrottle.AdjustedDripRate = 0;
}
});
}
else if (param == "request")
{
int newValue;
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
return;
int newCurrentThrottleKbps = newValue * 1000 / 8;
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
{
MainConsole.Instance.OutputFormat(
"Setting param {0} to {1} for {2} ({3}) in {4}",
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
udpClient.FlowThrottle.RequestedDripRate = newCurrentThrottleKbps;
}
});
}
else if (param == "max")
{
int newValue;
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
return;
int newThrottleMaxKbps = newValue * 1000 / 8;
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
{
MainConsole.Instance.OutputFormat(
"Setting param {0} to {1} for {2} ({3}) in {4}",
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
udpClient.FlowThrottle.MaxDripRate = newThrottleMaxKbps;
}
});
}
}
private void HandleThrottleGetCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
bool all = args.Length == 4;
bool one = args.Length == 6;
if (!all && !one)
{
MainConsole.Instance.OutputFormat(
"Usage: debug lludp throttles get [<avatar-first-name> <avatar-last-name>]");
return;
}
string firstName = null;
string lastName = null;
if (one)
{
firstName = args[4];
lastName = args[5];
}
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
{
m_console.OutputFormat(
"Status for {0} ({1}) in {2}",
sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
ConsoleDisplayList cdl = new ConsoleDisplayList();
cdl.AddRow("adaptive", udpClient.FlowThrottle.AdaptiveEnabled);
cdl.AddRow("current", string.Format("{0} kbps", udpClient.FlowThrottle.DripRate * 8 / 1000));
cdl.AddRow("request", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000));
cdl.AddRow("max", string.Format("{0} kbps", udpClient.FlowThrottle.MaxDripRate * 8 / 1000));
m_console.Output(cdl.ToString());
}
});
}
private void HandleGetCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
m_console.OutputFormat("Debug settings for {0}", m_udpServer.Scene.Name);
ConsoleDisplayList cdl = new ConsoleDisplayList();
long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
cdl.AddRow(
"max-scene-throttle",
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
int maxClientDripRate = m_udpServer.ThrottleRates.Total;
cdl.AddRow(
"max-new-client-throttle",
maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
m_console.Output(cdl.ToString());
}
private void HandleSetCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 5)
{
MainConsole.Instance.OutputFormat("Usage: debug lludp set <param> <value>");
return;
}
string param = args[3];
string rawValue = args[4];
int newValue;
if (param == "max-scene-throttle")
{
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
return;
m_udpServer.Throttle.MaxDripRate = newValue * 1000 / 8;
}
else if (param == "max-new-client-throttle")
{
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
return;
m_udpServer.ThrottleRates.Total = newValue * 1000 / 8;
}
else
{
return;
}
m_console.OutputFormat("{0} set to {1} in {2}", param, rawValue, m_udpServer.Scene.Name);
}
private void HandlePacketCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
bool setAsDefaultLevel = false;
bool setAll = false;
OptionSet optionSet = new OptionSet()
.Add("default", o => setAsDefaultLevel = (o != null))
.Add("all", o => setAll = (o != null));
List<string> filteredArgs = optionSet.Parse(args);
string name = null;
if (filteredArgs.Count == 6)
{
if (!(setAsDefaultLevel || setAll))
{
name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
}
else
{
MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
return;
}
}
if (filteredArgs.Count > 3)
{
int newDebug;
if (int.TryParse(filteredArgs[3], out newDebug))
{
if (setAsDefaultLevel || setAll)
{
m_udpServer.DefaultClientPacketDebugLevel = newDebug;
MainConsole.Instance.OutputFormat(
"Packet debug for {0} clients set to {1} in {2}",
(setAll ? "all" : "future"), m_udpServer.DefaultClientPacketDebugLevel, m_udpServer.Scene.Name);
if (setAll)
{
m_udpServer.Scene.ForEachScenePresence(sp =>
{
MainConsole.Instance.OutputFormat(
"Packet debug for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
sp.ControllingClient.DebugPacketLevel = newDebug;
});
}
}
else
{
m_udpServer.Scene.ForEachScenePresence(sp =>
{
if (name == null || sp.Name == name)
{
MainConsole.Instance.OutputFormat(
"Packet debug for {0} ({1}) set to {2} in {3}",
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
sp.ControllingClient.DebugPacketLevel = newDebug;
}
});
}
}
else
{
MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
}
}
}
private void HandleDropCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 6)
{
MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
return;
}
string direction = args[3];
string subCommand = args[4];
string packetName = args[5];
if (subCommand == "add")
{
MainConsole.Instance.OutputFormat(
"Adding packet {0} to {1} drop list for all connections in {2}",
direction, packetName, m_udpServer.Scene.Name);
m_udpServer.Scene.ForEachScenePresence(
sp =>
{
LLClientView llcv = (LLClientView)sp.ControllingClient;
if (direction == "in")
llcv.AddInPacketToDropSet(packetName);
else if (direction == "out")
llcv.AddOutPacketToDropSet(packetName);
}
);
}
else if (subCommand == "remove")
{
MainConsole.Instance.OutputFormat(
"Removing packet {0} from {1} drop list for all connections in {2}",
direction, packetName, m_udpServer.Scene.Name);
m_udpServer.Scene.ForEachScenePresence(
sp =>
{
LLClientView llcv = (LLClientView)sp.ControllingClient;
if (direction == "in")
llcv.RemoveInPacketFromDropSet(packetName);
else if (direction == "out")
llcv.RemoveOutPacketFromDropSet(packetName);
}
);
}
}
private void HandleStartCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
m_udpServer.StartInbound();
if (subCommand == "out" || subCommand == "all")
m_udpServer.StartOutbound();
}
private void HandleStopCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
m_udpServer.StopInbound();
if (subCommand == "out" || subCommand == "all")
m_udpServer.StopOutbound();
}
private void HandlePoolCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
return;
}
string enabled = args[3];
if (enabled == "on")
{
if (m_udpServer.EnablePools())
{
m_udpServer.EnablePoolStats();
MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name);
}
}
else if (enabled == "off")
{
if (m_udpServer.DisablePools())
{
m_udpServer.DisablePoolStats();
MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name);
}
}
else
{
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
}
}
private void HandleAgentUpdateCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
m_udpServer.DiscardInboundAgentUpdates = !m_udpServer.DiscardInboundAgentUpdates;
MainConsole.Instance.OutputFormat(
"Discard AgentUpdates now {0} for {1}", m_udpServer.DiscardInboundAgentUpdates, m_udpServer.Scene.Name);
}
private void HandleStatusCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
return;
MainConsole.Instance.OutputFormat(
"IN LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningInbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat(
"OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off");
MainConsole.Instance.OutputFormat(
"Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel);
}
}
}

View File

@ -135,7 +135,7 @@ 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 StartInbound(int recvBufferSize, bool asyncPacketHandling)
public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling)
{
m_asyncPacketHandling = asyncPacketHandling;
@ -168,6 +168,12 @@ namespace OpenMetaverse
m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
}
// On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment
// we never want two regions to listen on the same port as they cannot demultiplex each other's messages,
// leading to a confusing bug.
// By default, Windows does not allow two sockets to bind to the same port.
m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
if (recvBufferSize != 0)
m_udpSocket.ReceiveBufferSize = recvBufferSize;
@ -185,14 +191,14 @@ namespace OpenMetaverse
/// <summary>
/// Start outbound UDP packet handling.
/// </summary>
public void StartOutbound()
public virtual void StartOutbound()
{
m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
IsRunningOutbound = true;
}
public void StopInbound()
public virtual void StopInbound()
{
if (IsRunningInbound)
{
@ -203,14 +209,14 @@ namespace OpenMetaverse
}
}
public void StopOutbound()
public virtual void StopOutbound()
{
m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
IsRunningOutbound = false;
}
protected virtual bool EnablePools()
public virtual bool EnablePools()
{
if (!UsePools)
{
@ -224,7 +230,7 @@ namespace OpenMetaverse
return false;
}
protected virtual bool DisablePools()
public virtual bool DisablePools()
{
if (UsePools)
{

View File

@ -217,6 +217,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void ProcessRequests()
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
try
{
while (IsRunning || m_requestQueue.Count > 0)

View File

@ -36,7 +36,6 @@ using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
{
@ -47,7 +46,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
public class BasicCircuitTests : OpenSimTestCase
{
private Scene m_scene;
private TestLLUDPServer m_udpServer;
[TestFixtureSetUp]
public void FixtureInit()
@ -73,72 +71,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
StatsManager.SimExtraStats = new SimExtraStatsCollector();
}
/// <summary>
/// Build an object name packet for test purposes
/// </summary>
/// <param name="objectLocalId"></param>
/// <param name="objectName"></param>
private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
{
ObjectNamePacket onp = new ObjectNamePacket();
ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
odb.LocalID = objectLocalId;
odb.Name = Utils.StringToBytes(objectName);
onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
onp.Header.Zerocoded = false;
return onp;
}
private void AddUdpServer()
{
AddUdpServer(new IniConfigSource());
}
private void AddUdpServer(IniConfigSource configSource)
{
uint port = 0;
AgentCircuitManager acm = m_scene.AuthenticateHandler;
m_udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm);
m_udpServer.AddScene(m_scene);
}
/// <summary>
/// Used by tests that aren't testing this stage.
/// </summary>
private ScenePresence AddClient()
{
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
UUID mySessionUuid = TestHelpers.ParseTail(0x2);
uint myCircuitCode = 123456;
IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999);
UseCircuitCodePacket uccp = new UseCircuitCodePacket();
UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
= new UseCircuitCodePacket.CircuitCodeBlock();
uccpCcBlock.Code = myCircuitCode;
uccpCcBlock.ID = myAgentUuid;
uccpCcBlock.SessionID = mySessionUuid;
uccp.CircuitCode = uccpCcBlock;
byte[] uccpBytes = uccp.ToBytes();
UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length);
upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
AgentCircuitData acd = new AgentCircuitData();
acd.AgentID = myAgentUuid;
acd.SessionID = mySessionUuid;
m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
m_udpServer.PacketReceived(upb);
return m_scene.GetScenePresence(myAgentUuid);
}
// /// <summary>
// /// Build an object name packet for test purposes
// /// </summary>
// /// <param name="objectLocalId"></param>
// /// <param name="objectName"></param>
// private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
// {
// ObjectNamePacket onp = new ObjectNamePacket();
// ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
// odb.LocalID = objectLocalId;
// odb.Name = Utils.StringToBytes(objectName);
// onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
// onp.Header.Zerocoded = false;
//
// return onp;
// }
//
/// <summary>
/// Test adding a client to the stack
/// </summary>
@ -148,7 +97,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
AddUdpServer();
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene);
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
UUID mySessionUuid = TestHelpers.ParseTail(0x2);
@ -169,7 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
m_udpServer.PacketReceived(upb);
udpServer.PacketReceived(upb);
// Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet
Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null);
@ -180,15 +129,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
m_udpServer.PacketReceived(upb);
udpServer.PacketReceived(upb);
// Should succeed now
ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
Assert.That(sp.UUID, Is.EqualTo(myAgentUuid));
Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1));
Assert.That(udpServer.PacketsSent.Count, Is.EqualTo(1));
Packet packet = m_udpServer.PacketsSent[0];
Packet packet = udpServer.PacketsSent[0];
Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket)));
PacketAckPacket ackPacket = packet as PacketAckPacket;
@ -200,15 +149,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
public void TestLogoutClientDueToAck()
{
TestHelpers.InMethod();
TestHelpers.EnableLogging();
// TestHelpers.EnableLogging();
IniConfigSource ics = new IniConfigSource();
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
config.Set("AckTimeout", -1);
AddUdpServer(ics);
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene, ics);
ScenePresence sp = AddClient();
m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
ScenePresence sp
= ClientStackHelpers.AddChildClient(
m_scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID);
Assert.That(spAfterAckTimeout, Is.Null);

View File

@ -39,7 +39,6 @@ using OpenSim.Framework;
using OpenSim.Region.CoreModules.Agent.TextureSender;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
{

View File

@ -30,7 +30,6 @@ using NUnit.Framework;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenSim.Framework;
using OpenSim.Tests.Common.Mock;
using OpenSim.Tests.Common;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests

View File

@ -0,0 +1,425 @@
/*
* 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 Nini.Config;
using NUnit.Framework;
using OpenMetaverse.Packets;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
{
[TestFixture]
public class ThrottleTests : OpenSimTestCase
{
[TestFixtureSetUp]
public void FixtureInit()
{
// Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
}
[TestFixtureTearDown]
public void TearDown()
{
// We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
// threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
// tests really shouldn't).
Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
}
[Test]
public void TestSetRequestDripRate()
{
TestHelpers.InMethod();
TokenBucket tb = new TokenBucket("tb", null, 5000, 0);
AssertRates(tb, 5000, 0, 5000, 0);
tb.RequestedDripRate = 4000;
AssertRates(tb, 4000, 0, 4000, 0);
tb.RequestedDripRate = 6000;
AssertRates(tb, 6000, 0, 6000, 0);
}
[Test]
public void TestSetRequestDripRateWithMax()
{
TestHelpers.InMethod();
TokenBucket tb = new TokenBucket("tb", null, 5000, 10000);
AssertRates(tb, 5000, 0, 5000, 10000);
tb.RequestedDripRate = 4000;
AssertRates(tb, 4000, 0, 4000, 10000);
tb.RequestedDripRate = 6000;
AssertRates(tb, 6000, 0, 6000, 10000);
tb.RequestedDripRate = 12000;
AssertRates(tb, 10000, 0, 10000, 10000);
}
[Test]
public void TestSetRequestDripRateWithChildren()
{
TestHelpers.InMethod();
TokenBucket tbParent = new TokenBucket("tbParent", null, 0, 0);
TokenBucket tbChild1 = new TokenBucket("tbChild1", tbParent, 3000, 0);
TokenBucket tbChild2 = new TokenBucket("tbChild2", tbParent, 5000, 0);
AssertRates(tbParent, 8000, 8000, 8000, 0);
AssertRates(tbChild1, 3000, 0, 3000, 0);
AssertRates(tbChild2, 5000, 0, 5000, 0);
// Test: Setting a parent request greater than total children requests.
tbParent.RequestedDripRate = 10000;
AssertRates(tbParent, 10000, 8000, 8000, 0);
AssertRates(tbChild1, 3000, 0, 3000, 0);
AssertRates(tbChild2, 5000, 0, 5000, 0);
// Test: Setting a parent request lower than total children requests.
tbParent.RequestedDripRate = 6000;
AssertRates(tbParent, 6000, 8000, 6000, 0);
AssertRates(tbChild1, 3000, 0, 6000 / 8 * 3, 0);
AssertRates(tbChild2, 5000, 0, 6000 / 8 * 5, 0);
}
private void AssertRates(
TokenBucket tb, double requestedDripRate, double totalDripRequest, double dripRate, double maxDripRate)
{
Assert.AreEqual((int)requestedDripRate, tb.RequestedDripRate, "Requested drip rate");
Assert.AreEqual((int)totalDripRequest, tb.TotalDripRequest, "Total drip request");
Assert.AreEqual((int)dripRate, tb.DripRate, "Drip rate");
Assert.AreEqual((int)maxDripRate, tb.MaxDripRate, "Max drip rate");
}
[Test]
public void TestClientThrottleSetNoLimit()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
Scene scene = new SceneHelpers().SetupScene();
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
ScenePresence sp
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
udpServer.Throttle.DebugLevel = 1;
udpClient.ThrottleDebugLevel = 1;
int resendBytes = 1000;
int landBytes = 2000;
int windBytes = 3000;
int cloudBytes = 4000;
int taskBytes = 5000;
int textureBytes = 6000;
int assetBytes = 7000;
SetThrottles(
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
// We expect this to be lower because of the minimum bound set by MTU
int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
AssertThrottles(
udpClient,
LLUDPServer.MTU, landBytes, windBytes, cloudBytes, taskBytes,
textureBytes, assetBytes, totalBytes, 0, 0);
}
[Test]
public void TestClientThrottleAdaptiveNoLimit()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
Scene scene = new SceneHelpers().SetupScene();
IniConfigSource ics = new IniConfigSource();
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
config.Set("enable_adaptive_throttles", true);
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics);
ScenePresence sp
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
udpServer.Throttle.DebugLevel = 1;
udpClient.ThrottleDebugLevel = 1;
// Total is 280000
int resendBytes = 10000;
int landBytes = 20000;
int windBytes = 30000;
int cloudBytes = 40000;
int taskBytes = 50000;
int textureBytes = 60000;
int assetBytes = 70000;
int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
SetThrottles(
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
// Ratio of current adaptive drip rate to requested bytes
// XXX: Should hard code this as below so we don't rely on values given by tested code to construct
// expected values.
double commitRatio = (double)udpClient.FlowThrottle.AdjustedDripRate / udpClient.FlowThrottle.TargetDripRate;
AssertThrottles(
udpClient,
LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
// Test an increase in target throttle
udpClient.FlowThrottle.AcknowledgePackets(35000);
commitRatio = 0.2;
AssertThrottles(
udpClient,
resendBytes * commitRatio, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
// Test a decrease in target throttle
udpClient.FlowThrottle.ExpirePackets(1);
commitRatio = 0.1;
AssertThrottles(
udpClient,
LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
}
/// <summary>
/// Test throttle setttings where max client throttle has been limited server side.
/// </summary>
[Test]
public void TestSingleClientThrottleRegionLimited()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
int resendBytes = 6000;
int landBytes = 8000;
int windBytes = 10000;
int cloudBytes = 12000;
int taskBytes = 14000;
int textureBytes = 16000;
int assetBytes = 18000;
int totalBytes
= (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
Scene scene = new SceneHelpers().SetupScene();
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
udpServer.Throttle.RequestedDripRate = totalBytes;
ScenePresence sp1
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
SetThrottles(
udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
AssertThrottles(
udpClient1,
resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
textureBytes / 2, assetBytes / 2, totalBytes, 0, 0);
// Test: Now add another client
ScenePresence sp2
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
// udpClient.ThrottleDebugLevel = 1;
SetThrottles(
udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
AssertThrottles(
udpClient1,
resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
AssertThrottles(
udpClient2,
resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
}
/// <summary>
/// Test throttle setttings where max client throttle has been limited server side.
/// </summary>
[Test]
public void TestClientThrottlePerClientLimited()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
int resendBytes = 4000;
int landBytes = 6000;
int windBytes = 8000;
int cloudBytes = 10000;
int taskBytes = 12000;
int textureBytes = 14000;
int assetBytes = 16000;
int totalBytes
= (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
Scene scene = new SceneHelpers().SetupScene();
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
udpServer.ThrottleRates.Total = totalBytes;
ScenePresence sp
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
// udpClient.ThrottleDebugLevel = 1;
SetThrottles(
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
AssertThrottles(
udpClient,
resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
textureBytes / 2, assetBytes / 2, totalBytes, 0, totalBytes);
}
[Test]
public void TestClientThrottlePerClientAndRegionLimited()
{
TestHelpers.InMethod();
//TestHelpers.EnableLogging();
int resendBytes = 4000;
int landBytes = 6000;
int windBytes = 8000;
int cloudBytes = 10000;
int taskBytes = 12000;
int textureBytes = 14000;
int assetBytes = 16000;
// current total 70000
int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
Scene scene = new SceneHelpers().SetupScene();
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1);
udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5);
ScenePresence sp1
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
udpClient1.ThrottleDebugLevel = 1;
SetThrottles(
udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
AssertThrottles(
udpClient1,
resendBytes, landBytes, windBytes, cloudBytes, taskBytes,
textureBytes, assetBytes, totalBytes, 0, totalBytes * 1.1);
// Now add another client
ScenePresence sp2
= ClientStackHelpers.AddChildClient(
scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
udpClient2.ThrottleDebugLevel = 1;
SetThrottles(
udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
AssertThrottles(
udpClient1,
resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
AssertThrottles(
udpClient2,
resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
}
private void AssertThrottles(
LLUDPClient udpClient,
double resendBytes, double landBytes, double windBytes, double cloudBytes, double taskBytes, double textureBytes, double assetBytes,
double totalBytes, double targetBytes, double maxBytes)
{
ClientInfo ci = udpClient.GetClientInfo();
// Console.WriteLine(
// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
Assert.AreEqual((int)resendBytes, ci.resendThrottle, "Resend");
Assert.AreEqual((int)landBytes, ci.landThrottle, "Land");
Assert.AreEqual((int)windBytes, ci.windThrottle, "Wind");
Assert.AreEqual((int)cloudBytes, ci.cloudThrottle, "Cloud");
Assert.AreEqual((int)taskBytes, ci.taskThrottle, "Task");
Assert.AreEqual((int)textureBytes, ci.textureThrottle, "Texture");
Assert.AreEqual((int)assetBytes, ci.assetThrottle, "Asset");
Assert.AreEqual((int)totalBytes, ci.totalThrottle, "Total");
Assert.AreEqual((int)targetBytes, ci.targetThrottle, "Target");
Assert.AreEqual((int)maxBytes, ci.maxThrottle, "Max");
}
private void SetThrottles(
LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes)
{
byte[] throttles = new byte[28];
Array.Copy(BitConverter.GetBytes((float)resendBytes * 8), 0, throttles, 0, 4);
Array.Copy(BitConverter.GetBytes((float)landBytes * 8), 0, throttles, 4, 4);
Array.Copy(BitConverter.GetBytes((float)windBytes * 8), 0, throttles, 8, 4);
Array.Copy(BitConverter.GetBytes((float)cloudBytes * 8), 0, throttles, 12, 4);
Array.Copy(BitConverter.GetBytes((float)taskBytes * 8), 0, throttles, 16, 4);
Array.Copy(BitConverter.GetBytes((float)textureBytes * 8), 0, throttles, 20, 4);
Array.Copy(BitConverter.GetBytes((float)assetBytes * 8), 0, throttles, 24, 4);
udpClient.SetThrottles(throttles);
}
}
}

View File

@ -72,6 +72,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
// Current default total is 66750
Resend = throttleConfig.GetInt("resend_default", 6625);
Land = throttleConfig.GetInt("land_default", 9125);
Wind = throttleConfig.GetInt("wind_default", 1750);

View File

@ -42,9 +42,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public class TokenBucket
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static Int32 m_counter = 0;
// private Int32 m_identifier;
public string Identifier { get; private set; }
public int DebugLevel { get; set; }
/// <summary>
/// Number of ticks (ms) per quantum, drip rate and max burst
@ -75,20 +76,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Map of children buckets and their requested maximum burst rate
/// </summary>
protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
#region Properties
/// <summary>
/// The parent bucket of this bucket, or null if this bucket has no
/// parent. The parent bucket will limit the aggregate bandwidth of all
/// of its children buckets
/// </summary>
protected TokenBucket m_parent;
public TokenBucket Parent
{
get { return m_parent; }
set { m_parent = value; }
}
public TokenBucket Parent { get; protected set; }
/// <summary>
/// Maximum burst rate in bytes per second. This is the maximum number
@ -114,77 +108,106 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
/// <summary>
/// The speed limit of this bucket in bytes per second. This is the
/// number of tokens that are added to the bucket per quantum
/// The requested drip rate for this particular bucket.
/// </summary>
/// <remarks>Tokens are added to the bucket any time
/// <remarks>
/// 0 then TotalDripRequest is used instead.
/// Can never be above MaxDripRate.
/// Tokens are added to the bucket at any time
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
/// the system tick interval (typically around 15-22ms)</remarks>
protected Int64 m_dripRate;
/// the system tick interval (typically around 15-22ms)
/// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive
/// number on get if TotalDripRequest is sent. This also stops us being able to retrieve the fact that
/// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get
/// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties.
/// </remarks>
public virtual Int64 RequestedDripRate
{
get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
set {
m_dripRate = (value < 0 ? 0 : value);
get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
set
{
if (value <= 0)
m_dripRate = 0;
else if (MaxDripRate > 0 && value > MaxDripRate)
m_dripRate = MaxDripRate;
else
m_dripRate = value;
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
m_totalDripRequest = m_dripRate;
if (m_parent != null)
m_parent.RegisterRequest(this,m_dripRate);
if (Parent != null)
Parent.RegisterRequest(this, m_dripRate);
}
}
/// <summary>
/// Gets the drip rate.
/// </summary>
/// <value>
/// DripRate can never be above max drip rate or below min drip rate.
/// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the
/// parent bucket.
/// </value>
public virtual Int64 DripRate
{
get {
if (m_parent == null)
return Math.Min(RequestedDripRate,TotalDripRequest);
double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
get
{
double rate;
// FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set
// on ourselves which is not equal to the child drip rates.
if (Parent == null)
{
if (TotalDripRequest > 0)
rate = Math.Min(RequestedDripRate, TotalDripRequest);
else
rate = RequestedDripRate;
}
else
{
rate = (double)RequestedDripRate * Parent.DripRateModifier();
}
if (rate < m_minimumDripRate)
rate = m_minimumDripRate;
else if (MaxDripRate > 0 && rate > MaxDripRate)
rate = MaxDripRate;
return (Int64)rate;
}
}
protected Int64 m_dripRate;
// <summary>
// The maximum rate for flow control. Drip rate can never be greater than this.
// </summary>
public Int64 MaxDripRate { get; set; }
/// <summary>
/// The current total of the requested maximum burst rates of
/// this bucket's children buckets.
/// The current total of the requested maximum burst rates of children buckets.
/// </summary>
protected Int64 m_totalDripRequest;
public Int64 TotalDripRequest
{
get { return m_totalDripRequest; }
set { m_totalDripRequest = value; }
}
#endregion Properties
#region Constructor
public Int64 TotalDripRequest { get; protected set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="identifier">Identifier for this token bucket</param>
/// <param name="parent">Parent bucket if this is a child bucket, or
/// null if this is a root bucket</param>
/// <param name="maxBurst">Maximum size of the bucket in bytes, or
/// zero if this bucket has no maximum capacity</param>
/// <param name="dripRate">Rate that the bucket fills, in bytes per
/// second. If zero, the bucket always remains full</param>
public TokenBucket(TokenBucket parent, Int64 dripRate)
/// <param name="requestedDripRate">
/// Requested rate that the bucket fills, in bytes per
/// second. If zero, the bucket always remains full.
/// </param>
public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate)
{
// m_identifier = m_counter++;
m_counter++;
Identifier = identifier;
Parent = parent;
RequestedDripRate = dripRate;
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
RequestedDripRate = requestedDripRate;
MaxDripRate = maxDripRate;
m_lastDrip = Util.EnvironmentTickCount();
}
#endregion Constructor
/// <summary>
/// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
/// no modification if the requested bandwidth is less than the
@ -195,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected double DripRateModifier()
{
Int64 driprate = DripRate;
return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
// if (DebugLevel > 0)
// m_log.DebugFormat(
// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
// driprate, TotalDripRequest, modifier, Identifier);
return modifier;
}
/// <summary>
@ -217,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lock (m_children)
{
m_children[child] = request;
// m_totalDripRequest = m_children.Values.Sum();
m_totalDripRequest = 0;
TotalDripRequest = 0;
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
m_totalDripRequest += cref.Value;
TotalDripRequest += cref.Value;
}
// Pass the new values up to the parent
if (m_parent != null)
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
if (Parent != null)
{
Int64 effectiveDripRate;
if (RequestedDripRate > 0)
effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
else
effectiveDripRate = TotalDripRequest;
Parent.RegisterRequest(this, effectiveDripRate);
}
}
/// <summary>
@ -238,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lock (m_children)
{
m_children.Remove(child);
// m_totalDripRequest = m_children.Values.Sum();
m_totalDripRequest = 0;
TotalDripRequest = 0;
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
m_totalDripRequest += cref.Value;
TotalDripRequest += cref.Value;
}
// Pass the new values up to the parent
if (m_parent != null)
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
if (Parent != null)
Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
}
/// <summary>
@ -301,7 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// with no drip rate...
if (DripRate == 0)
{
m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier);
return;
}
@ -321,8 +357,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public class AdaptiveTokenBucket : TokenBucket
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public bool AdaptiveEnabled { get; set; }
/// <summary>
/// Target drip rate for this bucket.
/// </summary>
/// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks>
public Int64 TargetDripRate
{
get { return m_targetDripRate; }
set
{
m_targetDripRate = Math.Max(value, m_minimumFlow);
}
}
protected Int64 m_targetDripRate;
// <summary>
// Adjust drip rate in response to network conditions.
// </summary>
public virtual Int64 AdjustedDripRate
{
get { return m_dripRate; }
set
{
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate);
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
if (Parent != null)
Parent.RegisterRequest(this, m_dripRate);
}
}
/// <summary>
/// The minimum rate for flow control. Minimum drip rate is one
/// packet per second. Open the throttle to 15 packets per second
@ -330,65 +398,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
// <summary>
// The maximum rate for flow control. Drip rate can never be
// greater than this.
// </summary>
protected Int64 m_maxDripRate = 0;
public Int64 MaxDripRate
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, bool enabled)
: base(identifier, parent, requestedDripRate, maxDripRate)
{
get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
protected set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
}
AdaptiveEnabled = enabled;
public bool Enabled { get; private set; }
// <summary>
//
// </summary>
public virtual Int64 AdjustedDripRate
{
get { return m_dripRate; }
set {
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
if (m_parent != null)
m_parent.RegisterRequest(this,m_dripRate);
}
}
// <summary>
//
// </summary>
public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate)
{
Enabled = enabled;
if (Enabled)
if (AdaptiveEnabled)
{
// m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled");
MaxDripRate = maxDripRate;
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
TargetDripRate = m_minimumFlow;
AdjustedDripRate = m_minimumFlow;
}
}
// <summary>
//
// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
// </summary>
public void ExpirePackets(Int32 count)
{
// m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
if (Enabled)
if (AdaptiveEnabled)
{
if (DebugLevel > 0)
m_log.WarnFormat(
"[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}",
AdjustedDripRate, count, Identifier);
AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
}
}
// <summary>
//
// Reliable packets acked by the client adjust the drip rate up.
// </summary>
public void AcknowledgePackets(Int32 count)
{
if (Enabled)
if (AdaptiveEnabled)
AdjustedDripRate = AdjustedDripRate + count;
}
}
}
}

View File

@ -167,7 +167,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
// Do Decode!
if (decode)
Util.FireAndForget(delegate { Decode(assetID, j2kData); });
Util.FireAndForget(delegate { Decode(assetID, j2kData); }, null, "J2KDecoderModule.BeginDecode");
}
}

View File

@ -302,7 +302,7 @@ namespace OpenSim.Region.CoreModules.Asset
}
Util.FireAndForget(
delegate { WriteFileCache(filename, asset); });
delegate { WriteFileCache(filename, asset); }, null, "FlotsamAssetCache.UpdateFileCache");
}
}
catch (Exception e)

View File

@ -39,7 +39,6 @@ using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Asset.Tests
{

View File

@ -387,7 +387,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
if (!Enabled)
return false;
return AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
group.DetachFromBackup();
bool success = AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
if (!success)
group.AttachToBackup();
return success;
}
/// <summary>
@ -815,8 +822,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
"[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
so.DetachFromBackup();
// Remove from database and parcel prim count
m_scene.DeleteFromStorage(so.UUID);
m_scene.EventManager.TriggerParcelPrimCountTainted();

View File

@ -53,7 +53,6 @@ using OpenSim.Region.ScriptEngine.Interfaces;
using OpenSim.Region.ScriptEngine.XEngine;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
{
@ -199,6 +198,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
string attName = "att";
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
Assert.That(so.Backup, Is.True);
m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, true, false);
@ -213,6 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(attSo.IsAttachment);
Assert.That(attSo.UsesPhysics, Is.False);
Assert.That(attSo.IsTemporary, Is.False);
Assert.That(attSo.Backup, Is.False);
// Check item status
Assert.That(
@ -385,7 +386,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
public void TestRezAttachmentFromInventory()
{
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
// TestHelpers.EnableLogging();
Scene scene = CreateTestScene();
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
@ -407,6 +408,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(attSo.IsAttachment);
Assert.That(attSo.UsesPhysics, Is.False);
Assert.That(attSo.IsTemporary, Is.False);
Assert.IsFalse(attSo.Backup);
// Check appearance status
Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
@ -601,7 +603,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(scene.InventoryService.GetItem(new InventoryItemBase(attItem.ID)), Is.Null);
// Check object in scene
Assert.That(scene.GetSceneObjectGroup("att"), Is.Not.Null);
SceneObjectGroup soInScene = scene.GetSceneObjectGroup("att");
Assert.That(soInScene, Is.Not.Null);
Assert.IsTrue(soInScene.Backup);
// Check events
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
@ -755,6 +759,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(attSo.IsAttachment);
Assert.That(attSo.UsesPhysics, Is.False);
Assert.That(attSo.IsTemporary, Is.False);
Assert.IsFalse(attSo.Backup);
// Check appearance status
List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments();
@ -884,6 +889,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
Assert.IsFalse(actualSceneBAtt.Backup);
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
@ -994,6 +1000,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
Assert.IsFalse(actualSceneBAtt.Backup);
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));

View File

@ -593,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
if (sendTime < now)
{
Util.FireAndForget(o => SendAppearance(avatarID));
Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
m_sendqueue.Remove(avatarID);
}
}
@ -611,7 +611,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
if (sendTime < now)
{
Util.FireAndForget(o => SaveAppearance(avatarID));
Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
m_savequeue.Remove(avatarID);
}
}
@ -1038,7 +1038,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
else
m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
});
}, null, "AvatarFactoryModule.OnClientRequestWearables");
}
/// <summary>

View File

@ -34,7 +34,6 @@ using OpenSim.Framework;
using OpenSim.Region.CoreModules.Asset;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
{

View File

@ -187,7 +187,7 @@ namespace OpenSim.Region.CoreModules.Avatar.BakedTextures
{
rc.Request(reqStream, m_Auth);
m_log.DebugFormat("[XBakes]: stored {0} textures for user {1}", data.Length, agentId);
}
}, null, "XBakesModule.Store"
);
}
}

View File

@ -41,7 +41,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Chat.Tests
{

View File

@ -511,7 +511,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
// Notify about this user status
StatusNotify(friendList, agentID, online);
}
}, null, "FriendsModule.StatusChange"
);
}
}

View File

@ -660,7 +660,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
FriendsService.Delete(friendUUI, agentID.ToString());
// notify the exfriend's service
Util.FireAndForget(delegate { Delete(exfriendID, agentID, friendUUI); });
Util.FireAndForget(
delegate { Delete(exfriendID, agentID, friendUUI); }, null, "HGFriendsModule.DeleteFriendshipForeignFriend");
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI);
return true;
@ -678,7 +679,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
FriendsService.Delete(agentUUI, exfriendID.ToString());
// notify the agent's service?
Util.FireAndForget(delegate { Delete(agentID, exfriendID, agentUUI); });
Util.FireAndForget(
delegate { Delete(agentID, exfriendID, agentUUI); }, null, "HGFriendsModule.DeleteFriendshipLocalFriend");
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID);
return true;

View File

@ -35,7 +35,6 @@ using OpenSim.Framework;
using OpenSim.Region.CoreModules.Avatar.Friends;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
{

View File

@ -213,7 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
HandleUndeliverableMessage(im, result);
else
result(success);
});
}, null, "HGMessageTransferModule.SendInstantMessage");
return;
}

View File

@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
{

View File

@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
{

View File

@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
{

View File

@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
{

View File

@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
{

View File

@ -181,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
Util.FireAndForget(delegate
{
GetImageAssets(((IScenePresence)obj).UUID);
});
}, null, "UserProfileModule.GetImageAssets");
}
/// <summary>
@ -871,8 +871,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
object Pref = (object)pref;
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString()))
{
m_log.InfoFormat("[PROFILES]: UserPreferences request error");
remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
// m_log.InfoFormat("[PROFILES]: UserPreferences request error");
// remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
return;
}
pref = (UserPreferences) Pref;

View File

@ -804,8 +804,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// once we reach here...
//avatar.Scene.RemoveCapsHandler(avatar.UUID);
string capsPath = String.Empty;
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo();
agentCircuit.startpos = position;
@ -2702,5 +2700,69 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
#endregion
public virtual bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition)
{
// If the user is banned, we won't let any of their objects
// enter. Period.
//
if (Scene.RegionInfo.EstateSettings.IsBanned(so.OwnerID))
{
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Denied prim crossing of {0} {1} into {2} for banned avatar {3}",
so.Name, so.UUID, Scene.Name, so.OwnerID);
return false;
}
if (newPosition != Vector3.Zero)
so.RootPart.GroupPosition = newPosition;
if (!Scene.AddSceneObject(so))
{
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Problem adding scene object {0} {1} into {2} ",
so.Name, so.UUID, Scene.Name);
return false;
}
if (!so.IsAttachment)
{
// FIXME: It would be better to never add the scene object at all rather than add it and then delete
// it
if (!Scene.Permissions.CanObjectEntry(so.UUID, true, so.AbsolutePosition))
{
// Deny non attachments based on parcel settings
//
m_log.Info("[ENTITY TRANSFER MODULE]: Denied prim crossing because of parcel settings");
Scene.DeleteSceneObject(so, false);
return false;
}
// For attachments, we need to wait until the agent is root
// before we restart the scripts, or else some functions won't work.
so.RootPart.ParentGroup.CreateScriptInstances(
0, false, Scene.DefaultScriptEngine, GetStateSource(so));
so.ResumeScripts();
if (so.RootPart.KeyframeMotion != null)
so.RootPart.KeyframeMotion.UpdateSceneObject(so);
}
return true;
}
private int GetStateSource(SceneObjectGroup sog)
{
ScenePresence sp = Scene.GetScenePresence(sog.OwnerID);
if (sp != null)
return sp.GetStateSource();
return 2; // StateSource.PrimCrossing
}
}
}

View File

@ -110,6 +110,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
/// <summary>
/// Used for processing analysis of incoming attachments in a controlled fashion.
/// </summary>
private HGIncomingSceneObjectEngine m_incomingSceneObjectEngine;
#region ISharedRegionModule
public override string Name
@ -153,33 +158,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (m_Enabled)
{
scene.RegisterModuleInterface<IUserAgentVerificationModule>(this);
scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
}
}
//scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
void OnIncomingSceneObject(SceneObjectGroup so)
{
if (!so.IsAttachment)
return;
if (so.AttachedAvatar == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar))
return;
// foreign user
AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar);
if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
{
if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
{
string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url);
Dictionary<UUID, sbyte> ids = new Dictionary<UUID, sbyte>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(Scene.AssetService, url);
uuidGatherer.GatherAssetUuids(so, ids);
foreach (KeyValuePair<UUID, sbyte> kvp in ids)
uuidGatherer.FetchAsset(kvp.Key);
}
m_incomingSceneObjectEngine = new HGIncomingSceneObjectEngine(scene.Name);
m_incomingSceneObjectEngine.Start();
}
}
@ -209,12 +191,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
base.RemoveRegion(scene);
if (m_Enabled)
{
scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this);
m_incomingSceneObjectEngine.Stop();
}
}
#endregion
#region HG overrides of IEntiryTransferModule
#region HG overrides of IEntityTransferModule
protected override GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message)
{
@ -554,6 +539,132 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
private void RemoveIncomingSceneObjectJobs(string commonIdToRemove)
{
List<Job> jobsToReinsert = new List<Job>();
int jobsRemoved = 0;
Job job;
while ((job = m_incomingSceneObjectEngine.RemoveNextRequest()) != null)
{
if (job.CommonId != commonIdToRemove)
jobsToReinsert.Add(job);
else
jobsRemoved++;
}
m_log.DebugFormat(
"[HG ENTITY TRANSFER]: Removing {0} jobs with common ID {1} and reinserting {2} other jobs",
jobsRemoved, commonIdToRemove, jobsToReinsert.Count);
if (jobsToReinsert.Count > 0)
{
foreach (Job jobToReinsert in jobsToReinsert)
m_incomingSceneObjectEngine.QueueRequest(jobToReinsert);
}
}
public override bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition)
{
// FIXME: We must make it so that we can use SOG.IsAttachment here. At the moment it is always null!
if (!so.IsAttachmentCheckFull())
return base.HandleIncomingSceneObject(so, newPosition);
// Equally, we can't use so.AttachedAvatar here.
if (so.OwnerID == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.OwnerID))
return base.HandleIncomingSceneObject(so, newPosition);
// foreign user
AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.OwnerID);
if (aCircuit != null)
{
if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) == 0)
{
// We have already pulled the necessary attachments from the source grid.
base.HandleIncomingSceneObject(so, newPosition);
}
else
{
if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
{
m_incomingSceneObjectEngine.QueueRequest(
string.Format("HG UUID Gather for attachment {0} for {1}", so.Name, aCircuit.Name),
so.OwnerID.ToString(),
o =>
{
string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
// m_log.DebugFormat(
// "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset service {2}",
// so.Name, so.AttachedAvatar, url);
IteratingHGUuidGatherer uuidGatherer = new IteratingHGUuidGatherer(Scene.AssetService, url);
uuidGatherer.RecordAssetUuids(so);
while (!uuidGatherer.Complete)
{
int tickStart = Util.EnvironmentTickCount();
UUID? nextUuid = uuidGatherer.NextUuidToInspect;
uuidGatherer.GatherNext();
// m_log.DebugFormat(
// "[HG ENTITY TRANSFER]: Gathered attachment asset uuid {0} for object {1} for HG user {2} took {3} ms with asset service {4}",
// nextUuid, so.Name, so.OwnerID, Util.EnvironmentTickCountSubtract(tickStart), url);
int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart);
if (ticksElapsed > 30000)
{
m_log.WarnFormat(
"[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as gather of {1} from {2} took {3} ms to respond (> {4} ms)",
so.OwnerID, so.Name, url, ticksElapsed, 30000);
RemoveIncomingSceneObjectJobs(so.OwnerID.ToString());
return;
}
}
IDictionary<UUID, sbyte> ids = uuidGatherer.GetGatheredUuids();
// m_log.DebugFormat(
// "[HG ENTITY TRANSFER]: Fetching {0} assets for attachment {1} for HG user {2} with asset service {3}",
// ids.Count, so.Name, so.OwnerID, url);
foreach (KeyValuePair<UUID, sbyte> kvp in ids)
{
int tickStart = Util.EnvironmentTickCount();
uuidGatherer.FetchAsset(kvp.Key);
int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart);
if (ticksElapsed > 30000)
{
m_log.WarnFormat(
"[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as fetch of {1} from {2} took {3} ms to respond (> {4} ms)",
so.OwnerID, kvp.Key, url, ticksElapsed, 30000);
RemoveIncomingSceneObjectJobs(so.OwnerID.ToString());
return;
}
}
base.HandleIncomingSceneObject(so, newPosition);
// m_log.DebugFormat(
// "[HG ENTITY TRANSFER MODULE]: Completed incoming attachment {0} for HG user {1} with asset server {2}",
// so.Name, so.OwnerID, url);
},
null);
}
}
}
return true;
}
#endregion
#region IUserAgentVerificationModule

View File

@ -0,0 +1,344 @@
/*
* 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.Concurrent;
using System.Reflection;
using System.Threading;
using log4net;
using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
public class Job
{
public string Name { get; private set; }
public string CommonId { get; private set; }
public WaitCallback Callback { get; private set; }
public object O { get; private set; }
public Job(string name, string commonId, WaitCallback callback, object o)
{
Name = name;
CommonId = commonId;
Callback = callback;
O = o;
}
}
// TODO: These kinds of classes MUST be generalized with JobEngine, etc.
public class HGIncomingSceneObjectEngine
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public int LogLevel { get; set; }
public bool IsRunning { get; private set; }
public string Name { get; set; }
/// <summary>
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
/// </summary>
public int RequestProcessTimeoutOnStop { get; set; }
/// <summary>
/// Controls whether we need to warn in the log about exceeding the max queue size.
/// </summary>
/// <remarks>
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
/// order to avoid spamming the log with lots of warnings.
/// </remarks>
private bool m_warnOverMaxQueue = true;
private BlockingCollection<Job> m_requestQueue;
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
private Stat m_requestsWaitingStat;
private Job m_currentJob;
/// <summary>
/// Used to signal that we are ready to complete stop.
/// </summary>
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
public HGIncomingSceneObjectEngine(string name)
{
// LogLevel = 1;
Name = name;
RequestProcessTimeoutOnStop = 5000;
// MainConsole.Instance.Commands.AddCommand(
// "Debug",
// false,
// "debug jobengine",
// "debug jobengine <start|stop|status>",
// "Start, stop or get status of the job engine.",
// "If stopped then all jobs are processed immediately.",
// HandleControlCommand);
}
public void Start()
{
lock (this)
{
if (IsRunning)
return;
IsRunning = true;
m_finishedProcessingAfterStop.Reset();
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
m_requestsWaitingStat =
new Stat(
"HGIncomingAttachmentsWaiting",
"Number of incoming attachments waiting for processing.",
"",
"",
"entitytransfer",
Name,
StatType.Pull,
MeasuresOfInterest.None,
stat => stat.Value = m_requestQueue.Count,
StatVerbosity.Debug);
StatsManager.RegisterStat(m_requestsWaitingStat);
Watchdog.StartThread(
ProcessRequests,
string.Format("HG Incoming Scene Object Engine Thread ({0})", Name),
ThreadPriority.Normal,
false,
true,
null,
int.MaxValue);
}
}
public void Stop()
{
lock (this)
{
try
{
if (!IsRunning)
return;
IsRunning = false;
int requestsLeft = m_requestQueue.Count;
if (requestsLeft <= 0)
{
m_cancelSource.Cancel();
}
else
{
m_log.InfoFormat("[HG INCOMING SCENE OBJECT ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
while (requestsLeft > 0)
{
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
{
// After timeout no events have been written
if (requestsLeft == m_requestQueue.Count)
{
m_log.WarnFormat(
"[HG INCOMING SCENE OBJECT ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
RequestProcessTimeoutOnStop, requestsLeft);
break;
}
}
requestsLeft = m_requestQueue.Count;
}
}
}
finally
{
m_cancelSource.Dispose();
StatsManager.DeregisterStat(m_requestsWaitingStat);
m_requestsWaitingStat = null;
m_requestQueue = null;
}
}
}
public Job RemoveNextRequest()
{
Job nextRequest;
m_requestQueue.TryTake(out nextRequest);
return nextRequest;
}
public bool QueueRequest(string name, string commonId, WaitCallback req, object o)
{
return QueueRequest(new Job(name, commonId, req, o));
}
public bool QueueRequest(Job job)
{
if (LogLevel >= 1)
m_log.DebugFormat(
"[HG INCOMING SCENE OBJECT ENGINE]: Queued job {0}, common ID {1}", job.Name, job.CommonId);
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
{
// m_log.DebugFormat(
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
// categories, client.AgentID, m_udpServer.Scene.Name);
m_requestQueue.Add(job);
if (!m_warnOverMaxQueue)
m_warnOverMaxQueue = true;
return true;
}
else
{
if (m_warnOverMaxQueue)
{
// m_log.WarnFormat(
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
// client.AgentID, m_udpServer.Scene.Name);
m_log.WarnFormat("[HG INCOMING SCENE OBJECT ENGINE]: Request queue at maximum capacity, not recording job");
m_warnOverMaxQueue = false;
}
return false;
}
}
private void ProcessRequests()
{
try
{
while (IsRunning || m_requestQueue.Count > 0)
{
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
// QueueEmpty callback = req.Client.OnQueueEmpty;
//
// if (callback != null)
// {
// try
// {
// callback(req.Categories);
// }
// catch (Exception e)
// {
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
// }
// }
if (LogLevel >= 1)
m_log.DebugFormat("[HG INCOMING SCENE OBJECT ENGINE]: Processing job {0}", m_currentJob.Name);
try
{
m_currentJob.Callback.Invoke(m_currentJob.O);
}
catch (Exception e)
{
m_log.Error(
string.Format(
"[HG INCOMING SCENE OBJECT ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
}
if (LogLevel >= 1)
m_log.DebugFormat("[HG INCOMING SCENE OBJECT ENGINE]: Processed job {0}", m_currentJob.Name);
m_currentJob = null;
}
}
catch (OperationCanceledException)
{
}
m_finishedProcessingAfterStop.Set();
}
// private void HandleControlCommand(string module, string[] args)
// {
// // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
// // return;
//
// if (args.Length < 3)
// {
// MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|loglevel>");
// return;
// }
//
// string subCommand = args[2];
//
// if (subCommand == "stop")
// {
// Stop();
// MainConsole.Instance.OutputFormat("Stopped job engine.");
// }
// else if (subCommand == "start")
// {
// Start();
// MainConsole.Instance.OutputFormat("Started job engine.");
// }
// else if (subCommand == "status")
// {
// MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
// MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
// MainConsole.Instance.OutputFormat(
// "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
// MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
// }
//
// else if (subCommand == "loglevel")
// {
// // int logLevel;
// int logLevel = int.Parse(args[3]);
// // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
// // {
// LogLevel = logLevel;
// MainConsole.Instance.OutputFormat("Set log level to {0}", LogLevel);
// // }
// }
// else
// {
// MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
// }
// }
}
}

View File

@ -189,50 +189,203 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
return Utils.StringToBytes(RewriteSOP(xml));
}
protected string RewriteSOP(string xml)
protected void TransformXml(XmlReader reader, XmlWriter writer)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
// m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML");
foreach (XmlNode sop in sops)
int sopDepth = -1;
UserAccount creator = null;
bool hasCreatorData = false;
while (reader.Read())
{
UserAccount creator = null;
bool hasCreatorData = false;
XmlNodeList nodes = sop.ChildNodes;
foreach (XmlNode node in nodes)
//Console.WriteLine("Depth: {0}", reader.Depth);
switch (reader.NodeType)
{
if (node.Name == "CreatorID")
case XmlNodeType.Attribute:
writer.WriteAttributeString(reader.Prefix, reader.Name, reader.NamespaceURI, reader.Value);
break;
case XmlNodeType.CDATA:
writer.WriteCData(reader.Value);
break;
case XmlNodeType.Comment:
writer.WriteComment(reader.Value);
break;
case XmlNodeType.DocumentType:
writer.WriteDocType(reader.Name, reader.Value, null, null);
break;
case XmlNodeType.Element:
// m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name);
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
if (reader.LocalName == "SceneObjectPart")
{
UUID uuid = UUID.Zero;
UUID.TryParse(node.InnerText, out uuid);
creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
if (sopDepth < 0)
{
sopDepth = reader.Depth;
// m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth);
}
}
if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty)
hasCreatorData = true;
else
{
if (sopDepth >= 0 && reader.Depth == sopDepth + 1)
{
if (reader.Name == "CreatorID")
{
reader.Read();
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID")
{
reader.Read();
//if (node.Name == "OwnerID")
//{
// UserAccount owner = GetUser(node.InnerText);
// if (owner != null)
// node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
//}
}
if (reader.NodeType == XmlNodeType.Text)
{
UUID uuid = UUID.Zero;
UUID.TryParse(reader.Value, out uuid);
creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
writer.WriteElementString("UUID", reader.Value);
reader.Read();
}
else
{
// If we unexpected run across mixed content in this node, still carry on
// transforming the subtree (this replicates earlier behaviour).
TransformXml(reader, writer);
}
}
else
{
// If we unexpected run across mixed content in this node, still carry on
// transforming the subtree (this replicates earlier behaviour).
TransformXml(reader, writer);
}
}
else if (reader.Name == "CreatorData")
{
reader.Read();
if (reader.NodeType == XmlNodeType.Text)
{
hasCreatorData = true;
writer.WriteString(reader.Value);
}
else
{
// If we unexpected run across mixed content in this node, still carry on
// transforming the subtree (this replicates earlier behaviour).
TransformXml(reader, writer);
}
}
}
}
if (reader.IsEmptyElement)
{
// m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name);
writer.WriteEndElement();
}
if (!hasCreatorData && creator != null)
{
XmlElement creatorData = doc.CreateElement("CreatorData");
creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName;
sop.AppendChild(creatorData);
break;
case XmlNodeType.EndElement:
// m_log.DebugFormat("Depth {0} at EndElement", reader.Depth);
if (sopDepth == reader.Depth)
{
if (!hasCreatorData && creator != null)
writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", m_HomeURI, creator.FirstName, creator.LastName));
// m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth");
sopDepth = -1;
creator = null;
hasCreatorData = false;
}
writer.WriteEndElement();
break;
case XmlNodeType.EntityReference:
writer.WriteEntityRef(reader.Name);
break;
case XmlNodeType.ProcessingInstruction:
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
case XmlNodeType.Text:
writer.WriteString(reader.Value);
break;
default:
m_log.WarnFormat("[HG ASSET MAPPER]: Unrecognized node in asset XML transform in {0}", m_scene.Name);
break;
}
}
}
using (StringWriter wr = new StringWriter())
protected string RewriteSOP(string xmlData)
{
// Console.WriteLine("Input XML [{0}]", xmlData);
using (StringWriter sw = new StringWriter())
using (XmlTextWriter writer = new XmlTextWriter(sw))
using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null))
using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment }))
{
doc.Save(wr);
return wr.ToString();
TransformXml(reader, writer);
writer.WriteEndDocument();
// Console.WriteLine("Output: [{0}]", sw.ToString());
return sw.ToString();
}
// We are now taking the more complex streaming approach above because some assets can be very large
// and can trigger higher CPU use or possibly memory problems.
// XmlDocument doc = new XmlDocument();
// doc.LoadXml(xml);
// XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
//
// foreach (XmlNode sop in sops)
// {
// UserAccount creator = null;
// bool hasCreatorData = false;
// XmlNodeList nodes = sop.ChildNodes;
// foreach (XmlNode node in nodes)
// {
// if (node.Name == "CreatorID")
// {
// UUID uuid = UUID.Zero;
// UUID.TryParse(node.InnerText, out uuid);
// creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
// }
// if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty)
// hasCreatorData = true;
//
// //if (node.Name == "OwnerID")
// //{
// // UserAccount owner = GetUser(node.InnerText);
// // if (owner != null)
// // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
// //}
// }
//
// if (!hasCreatorData && creator != null)
// {
// XmlElement creatorData = doc.CreateElement("CreatorData");
// creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName;
// sop.AppendChild(creatorData);
// }
// }
//
// using (StringWriter wr = new StringWriter())
// {
// doc.Save(wr);
// return wr.ToString();
// }
}
// TODO: unused

View File

@ -912,7 +912,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
// one full update during the attachment
// process causes some clients to fail to display the
// attachment properly.
m_Scene.AddNewSceneObject(group, true, false);
m_Scene.AddNewSceneObject(group, !attachment, false);
// if attachment we set it's asset id so object updates
// can reflect that, if not, we set it's position in world.

View File

@ -0,0 +1,85 @@
/*
* 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.Xml;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Framework.InventoryAccess;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
{
[TestFixture]
public class HGAssetMapperTests : OpenSimTestCase
{
[Test]
public void TestPostAssetRewrite()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
string homeUrl = "http://hg.HomeTestPostAssetRewriteGrid.com";
string foreignUrl = "http://hg.ForeignTestPostAssetRewriteGrid.com";
UUID assetId = TestHelpers.ParseTail(0x1);
UUID userId = TestHelpers.ParseTail(0x10);
string userFirstName = "TestPostAsset";
string userLastName = "Rewrite";
int soPartsCount = 3;
Scene scene = new SceneHelpers().SetupScene();
HGAssetMapper hgam = new HGAssetMapper(scene, homeUrl);
UserAccount ua
= UserAccountHelpers.CreateUserWithInventory(scene, userFirstName, userLastName, userId, "password");
//AssetBase ncAssetSet = AssetHelpers.CreateNotecardAsset(assetId, "TestPostAssetRewriteNotecard");
SceneObjectGroup so = SceneHelpers.CreateSceneObject(soPartsCount, ua.PrincipalID);
AssetBase ncAssetSet = AssetHelpers.CreateAsset(assetId, so);
ncAssetSet.CreatorID = foreignUrl;
hgam.PostAsset(foreignUrl, ncAssetSet);
AssetBase ncAssetGet = scene.AssetService.Get(assetId.ToString());
Assert.AreEqual(foreignUrl, ncAssetGet.CreatorID);
string xmlData = Utils.BytesToString(ncAssetGet.Data);
XmlDocument ncAssetGetXmlDoc = new XmlDocument();
ncAssetGetXmlDoc.LoadXml(xmlData);
XmlNodeList creatorDataNodes = ncAssetGetXmlDoc.GetElementsByTagName("CreatorData");
Assert.AreEqual(soPartsCount, creatorDataNodes.Count);
//Console.WriteLine("creatorDataNodes {0}", creatorDataNodes.Count);
foreach (XmlNode creatorDataNode in creatorDataNodes)
{
Assert.AreEqual(
string.Format("{0};{1} {2}", homeUrl, ua.FirstName, ua.LastName), creatorDataNode.InnerText);
}
}
}
}

View File

@ -44,7 +44,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
{

View File

@ -32,7 +32,6 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Framework.UserManagement;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Framework.UserManagement.Tests
{

View File

@ -41,7 +41,6 @@ using OpenSim.Framework;
using OpenSim.Region.CoreModules.Scripting.HttpRequest;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests
{

View File

@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.Scripting.VectorRender;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
{

View File

@ -69,6 +69,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
get { return "HGAssetBroker"; }
}
public HGAssetBroker() {}
public HGAssetBroker(IConfigSource config)
{
Initialise(config);
}
public void Initialise(IConfigSource source)
{
IConfig moduleConfig = source.Configs["Modules"];
@ -288,7 +295,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
if (asset != null)
{
Util.FireAndForget(delegate { handler(id, sender, asset); });
Util.FireAndForget(delegate { handler(id, sender, asset); }, null, "HGAssetBroker.GotFromCache");
return true;
}

View File

@ -236,7 +236,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
if (asset != null)
{
Util.FireAndForget(delegate { handler(id, sender, asset); });
Util.FireAndForget(
o => handler(id, sender, asset), null, "LocalAssetServiceConnector.GotFromCacheCallback");
return true;
}
}
@ -249,7 +250,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
// if (null == a)
// m_log.WarnFormat("[LOCAL ASSET SERVICES CONNECTOR]: Could not asynchronously find asset with id {0}", id);
Util.FireAndForget(delegate { handler(assetID, s, a); });
Util.FireAndForget(
o => handler(assetID, s, a), null, "LocalAssetServiceConnector.GotFromServiceCallback");
});
}

View File

@ -198,8 +198,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
{
GridRegion region = null;
uint regionX = Util.WorldToRegionLoc((uint)x);
uint regionY = Util.WorldToRegionLoc((uint)y);
// uint regionX = Util.WorldToRegionLoc((uint)x);
// uint regionY = Util.WorldToRegionLoc((uint)y);
// First see if it's a neighbour, even if it isn't on this sim.
// Neighbour data is cached in memory, so this is fast

View File

@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
/// </remarks>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
public class MapImageServiceModule : ISharedRegionModule
public class MapImageServiceModule : IMapImageUploadModule, ISharedRegionModule
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -152,6 +152,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
m_scenes[scene.RegionInfo.RegionID] = scene;
scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
scene.RegisterModuleInterface<IMapImageUploadModule>(this);
}
///<summary>
@ -198,14 +200,53 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
m_lastrefresh = Util.EnvironmentTickCount();
}
public void UploadMapTile(IScene scene, Bitmap mapTile)
{
m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.Name);
// mapTile.Save( // DEBUG DEBUG
// String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
// ImageFormat.Jpeg);
// If the region/maptile is legacy sized, just upload the one tile like it has always been done
if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
{
ConvertAndUploadMaptile(mapTile,
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
scene.RegionInfo.RegionName);
}
else
{
// For larger regions (varregion) we must cut the region image into legacy sized
// pieces since that is how the maptile system works.
// Note the assumption that varregions are always a multiple of legacy size.
for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
{
for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
{
// Images are addressed from the upper left corner so have to do funny
// math to pick out the sub-tile since regions are numbered from
// the lower left.
Rectangle rect = new Rectangle(
(int)xx,
mapTile.Height - (int)yy - (int)Constants.RegionSize,
(int)Constants.RegionSize, (int)Constants.RegionSize);
using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
{
ConvertAndUploadMaptile(subMapTile,
scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
scene.Name);
}
}
}
}
}
///<summary>
///
///</summary>
private void UploadMapTile(IScene scene)
{
m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName);
string regionName = scene.RegionInfo.RegionName;
// Create a JPG map tile and upload it to the AddMapTile API
IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
if (tileGenerator == null)
@ -213,46 +254,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
return;
}
using (Bitmap mapTile = tileGenerator.CreateMapTile())
{
if (mapTile != null)
{
// mapTile.Save( // DEBUG DEBUG
// String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
// ImageFormat.Jpeg);
// If the region/maptile is legacy sized, just upload the one tile like it has always been done
if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
{
ConvertAndUploadMaptile(mapTile,
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
scene.RegionInfo.RegionName);
}
else
{
// For larger regions (varregion) we must cut the region image into legacy sized
// pieces since that is how the maptile system works.
// Note the assumption that varregions are always a multiple of legacy size.
for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
{
for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
{
// Images are addressed from the upper left corner so have to do funny
// math to pick out the sub-tile since regions are numbered from
// the lower left.
Rectangle rect = new Rectangle(
(int)xx,
mapTile.Height - (int)yy - (int)Constants.RegionSize,
(int)Constants.RegionSize, (int)Constants.RegionSize);
using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
{
ConvertAndUploadMaptile(subMapTile,
scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
regionName);
}
}
}
}
UploadMapTile(scene, mapTile);
}
else
{

View File

@ -45,7 +45,6 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;

View File

@ -31,7 +31,6 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.World.Land.Tests
{

View File

@ -36,7 +36,6 @@ using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.World.Land.Tests
{

View File

@ -39,7 +39,6 @@ using OpenSim.Region.CoreModules.World.Media.Moap;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
{

View File

@ -141,6 +141,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.BuySell
part.ObjectSaleType = 0;
part.SalePrice = 10;
part.ClickAction = Convert.ToByte(0);
group.HasGroupChanged = true;
part.SendPropertiesToClient(remoteClient);

View File

@ -87,7 +87,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
"Regions", false, "show region",
"show region",
"Show control information for the currently selected region (host name, max physical prim size, etc).",
"A synonym for \"region get\"",
HandleShowRegion);
m_console.Commands.AddCommand(
"Regions", false, "region get",
"region get",
"Show control information for the currently selected region (host name, max physical prim size, etc).",
"Some parameters can be set with the \"region set\" command.\n"
+ "Others must be changed via a viewer (usually via the region/estate dialog box).",
HandleShowRegion);
m_console.Commands.AddCommand(
"Regions", false, "region set",
"region get",
"Set control information for the currently selected region.",
"Currently, the following parameters can be set:\n"
+ "agent-limit <int> - Current root agent limit. This is persisted over restart.\n"
+ "max-agent-limit <int> - Maximum root agent limit. agent-limit cannot exceed this."
+ " This is not persisted over restart - to set it every time you must add a MaxAgents entry to your regions file.",
HandleRegionSet);
}
public void RemoveRegion(Scene scene)
@ -123,8 +142,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
dispList.AddRow("External endpoint", ri.ExternalEndPoint);
dispList.AddRow("Internal endpoint", ri.InternalEndPoint);
dispList.AddRow("Access level", ri.AccessLevel);
dispList.AddRow("Agent limit", rs.AgentLimit);
dispList.AddRow("Max agent limit", ri.AgentCapacity);
dispList.AddRow("Current agent limit", rs.AgentLimit);
dispList.AddRow("Linkset capacity", ri.LinksetCapacity <= 0 ? "not set" : ri.LinksetCapacity.ToString());
dispList.AddRow("Prim capacity", ri.ObjectCapacity);
dispList.AddRow("Prim bonus", rs.ObjectBonus);
@ -166,6 +185,73 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
MainConsole.Instance.Output(sb.ToString());
}
private void HandleRegionSet(string module, string[] args)
{
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
return;
if (args.Length != 4)
{
MainConsole.Instance.OutputFormat("Usage: region set <param> <value>");
return;
}
string param = args[2];
string rawValue = args[3];
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
return;
RegionInfo ri = m_scene.RegionInfo;
RegionSettings rs = ri.RegionSettings;
if (param == "agent-limit")
{
int newValue;
if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, rawValue, out newValue))
return;
if (newValue > ri.AgentCapacity)
{
MainConsole.Instance.OutputFormat(
"Cannot set {0} to {1} in {2} as max-agent-limit is {3}", "agent-limit",
newValue, m_scene.Name, ri.AgentCapacity);
}
else
{
rs.AgentLimit = newValue;
MainConsole.Instance.OutputFormat(
"{0} set to {1} in {2}", "agent-limit", newValue, m_scene.Name);
}
rs.Save();
}
else if (param == "max-agent-limit")
{
int newValue;
if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, rawValue, out newValue))
return;
ri.AgentCapacity = newValue;
MainConsole.Instance.OutputFormat(
"{0} set to {1} in {2}", "max-agent-limit", newValue, m_scene.Name);
if (ri.AgentCapacity < rs.AgentLimit)
{
rs.AgentLimit = ri.AgentCapacity;
MainConsole.Instance.OutputFormat(
"Reducing {0} to {1} in {2}", "agent-limit", rs.AgentLimit, m_scene.Name);
}
rs.Save();
}
}
private void HandleShowScene(string module, string[] cmd)
{
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))

View File

@ -68,6 +68,9 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
private static readonly UUID STOP_UUID = UUID.Random();
private static readonly string m_mapLayerPath = "0001/";
private IMapImageGenerator m_mapImageGenerator;
private IMapImageUploadModule m_mapImageServiceModule;
private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
protected Scene m_scene;
@ -100,7 +103,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
= Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000;
}
public virtual void AddRegion (Scene scene)
public virtual void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
@ -144,8 +147,10 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
return;
m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>();
}
m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>();
m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>();
}
public virtual void Close()
{
@ -1315,7 +1320,17 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (consoleScene != null && consoleScene != m_scene)
return;
GenerateMaptile();
if (m_mapImageGenerator == null)
{
Console.WriteLine("No map image generator available for {0}", m_scene.Name);
return;
}
using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
{
GenerateMaptile(mapbmp);
m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp);
}
}
public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
@ -1444,16 +1459,25 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (m_scene.Heightmap == null)
return;
//create a texture asset of the terrain
IMapImageGenerator terrain = m_scene.RequestModuleInterface<IMapImageGenerator>();
if (terrain == null)
return;
m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name);
m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
GenerateMaptile(mapbmp);
}
byte[] data = terrain.WriteJpeg2000Image();
if (data == null)
private void GenerateMaptile(Bitmap mapbmp)
{
byte[] data;
try
{
data = OpenJPEG.EncodeFromImage(mapbmp, true);
}
catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke
{
m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e);
return;
}
byte[] overlay = GenerateOverlay();

View File

@ -98,6 +98,8 @@ namespace OpenSim.Region.Framework.Interfaces
void Cross(SceneObjectGroup sog, Vector3 position, bool silent);
ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, string version);
bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition);
}
public interface IUserAgentVerificationModule

View File

@ -25,55 +25,13 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Net;
using OpenMetaverse;
using System.Drawing;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
namespace OpenSim.Region.Framework.Interfaces
{
/// <summary>
/// Mock scene for unit tests
/// </summary>
public class MockScene : SceneBase
public interface IMapImageUploadModule
{
public int ObjectNameCallsReceived
{
get { return m_objectNameCallsReceived; }
}
protected int m_objectNameCallsReceived;
public MockScene() : base(new RegionInfo(1000, 1000, null, null))
{
m_regStatus = RegionStatus.Up;
}
public override bool Update(int frames) { return true; }
public override void LoadWorldMap() {}
public override ISceneAgent AddNewAgent(IClientAPI client, PresenceType type)
{
client.OnObjectName += RecordObjectNameCall;
// FIXME
return null;
}
public override bool CloseAgent(UUID agentID, bool force) { return true; }
public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; }
public override void OtherRegionUp(GridRegion otherRegion) { }
public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; }
/// <summary>
/// Doesn't really matter what the call is - we're using this to test that a packet has actually been received
/// </summary>
protected void RecordObjectNameCall(IClientAPI remoteClient, uint localID, string message)
{
m_objectNameCallsReceived++;
}
void UploadMapTile(IScene scene, Bitmap mapTile);
}
}
}

View File

@ -1227,16 +1227,21 @@ namespace OpenSim.Region.Framework.Scenes
agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
if (taskItem.InvType == (int)InventoryType.Object)
{
uint perms = taskItem.CurrentPermissions;
// Bake the new base permissions from folded permissions
// The folded perms are in the lowest 3 bits of the current perms
// We use base permissions here to avoid baking the "Locked" status
// into the item as it is passed.
uint perms = taskItem.BasePermissions & taskItem.NextPermissions;
PermissionsUtil.ApplyFoldedPermissions(taskItem.CurrentPermissions, ref perms);
// Avoid the "lock trap" - move must always be enabled but the above may remove it
// Add it back here.
agentItem.BasePermissions = perms | (uint)PermissionMask.Move;
agentItem.CurrentPermissions = agentItem.BasePermissions;
}
else
{
agentItem.CurrentPermissions = agentItem.BasePermissions & taskItem.CurrentPermissions;
// Newly given items cannot be "locked" on rez. Make sure by
// setting current equal to base.
}
agentItem.CurrentPermissions = agentItem.BasePermissions;
agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
agentItem.NextPermissions = taskItem.NextPermissions;
agentItem.EveryOnePermissions = taskItem.EveryonePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
@ -1935,8 +1940,11 @@ namespace OpenSim.Region.Framework.Scenes
/// Rez a script into a prim's inventory from another prim
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="itemID"> </param>
/// <param name="localID"></param>
/// <param name="srcPart"> </param>
/// <param name="destId"> </param>
/// <param name="pin"></param>
/// <param name="running"></param>
/// <param name="start_param"></param>
public void RezScriptFromPrim(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param)
{
TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId);
@ -1956,12 +1964,11 @@ namespace OpenSim.Region.Framework.Scenes
if (destPart == null)
{
m_log.ErrorFormat(
"[PRIM INVENTORY]: " +
"Could not find script for ID {0}",
destId);
"[PRIM INVENTORY]: Could not find part {0} to insert script item {1} from {2} {3} in {4}",
destId, srcId, srcPart.Name, srcPart.UUID, Name);
return;
}
// Must own the object, and have modify rights
if (srcPart.OwnerID != destPart.OwnerID)
{
@ -1969,12 +1976,14 @@ namespace OpenSim.Region.Framework.Scenes
if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) ||
((destPart.GroupMask & (uint)PermissionMask.Modify) == 0))
return;
} else {
}
else
{
if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0)
return;
}
if (destPart.ScriptAccessPin != pin)
if (destPart.ScriptAccessPin == 0 || destPart.ScriptAccessPin != pin)
{
m_log.WarnFormat(
"[PRIM INVENTORY]: " +

View File

@ -6,7 +6,7 @@
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* * 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
@ -360,30 +360,52 @@ namespace OpenSim.Region.Framework.Scenes
public uint MaintenanceRun { get; private set; }
/// <summary>
/// The minimum length of time in seconds that will be taken for a scene frame. If the frame takes less time then we
/// The minimum length of time in milliseconds that will be taken for a scene frame. If the frame takes less time then we
/// will sleep for the remaining period.
/// </summary>
/// <remarks>
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
/// occur too quickly (viewer 1) or with even more slide (viewer 2).
/// </remarks>
public float MinFrameTime { get; private set; }
public int MinFrameTicks
{
get { return m_minFrameTicks; }
private set
{
m_minFrameTicks = value;
MinFrameSeconds = (float)m_minFrameTicks / 1000;
}
}
private int m_minFrameTicks;
/// <summary>
/// The minimum length of time in seconds that will be taken for a maintenance run.
/// The minimum length of time in seconds that will be taken for a scene frame.
/// </summary>
public float MinMaintenanceTime { get; private set; }
/// <remarks>
/// Always derived from MinFrameTicks.
/// </remarks>
public float MinFrameSeconds { get; private set; }
/// <summary>
/// The minimum length of time in milliseconds that will be taken for a scene frame. If the frame takes less time then we
/// will sleep for the remaining period.
/// </summary>
/// <remarks>
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
/// occur too quickly (viewer 1) or with even more slide (viewer 2).
/// </remarks>
public int MinMaintenanceTicks { get; set; }
private int m_update_physics = 1;
private int m_update_entitymovement = 1;
private int m_update_objects = 1;
private int m_update_temp_cleaning = 1000;
private int m_update_presences = 1; // Update scene presence movements
private int m_update_events = 1;
private int m_update_backup = 200;
private int m_update_terrain = 50;
// private int m_update_land = 1;
private int m_update_coarse_locations = 50;
private int m_update_temp_cleaning = 180;
private int agentMS;
private int frameMS;
@ -412,8 +434,16 @@ namespace OpenSim.Region.Framework.Scenes
/// asynchronously from the update loop.
/// </summary>
private bool m_cleaningTemps = false;
/// <summary>
/// Used to control main scene thread looping time when not updating via timer.
/// </summary>
private ManualResetEvent m_updateWaitEvent = new ManualResetEvent(false);
// private Object m_heartbeatLock = new Object();
/// <summary>
/// Used to control maintenance thread runs.
/// </summary>
private ManualResetEvent m_maintenanceWaitEvent = new ManualResetEvent(false);
// TODO: Possibly stop other classes being able to manipulate this directly.
private SceneGraph m_sceneGraph;
@ -782,8 +812,8 @@ namespace OpenSim.Region.Framework.Scenes
: this(regInfo, physicsScene)
{
m_config = config;
MinFrameTime = 0.089f;
MinMaintenanceTime = 1;
MinFrameTicks = 89;
MinMaintenanceTicks = 1000;
SeeIntoRegion = true;
Random random = new Random();
@ -1005,7 +1035,9 @@ namespace OpenSim.Region.Framework.Scenes
}
}
MinFrameTime = startupConfig.GetFloat( "MinFrameTime", MinFrameTime);
if (startupConfig.Contains("MinFrameTime"))
MinFrameTicks = (int)(startupConfig.GetFloat("MinFrameTime") * 1000);
m_update_backup = startupConfig.GetInt( "UpdateStorageEveryNFrames", m_update_backup);
m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations);
m_update_entitymovement = startupConfig.GetInt( "UpdateEntityMovementEveryNFrames", m_update_entitymovement);
@ -1014,11 +1046,11 @@ namespace OpenSim.Region.Framework.Scenes
m_update_physics = startupConfig.GetInt( "UpdatePhysicsEveryNFrames", m_update_physics);
m_update_presences = startupConfig.GetInt( "UpdateAgentsEveryNFrames", m_update_presences);
m_update_terrain = startupConfig.GetInt( "UpdateTerrainEveryNFrames", m_update_terrain);
m_update_temp_cleaning = startupConfig.GetInt( "UpdateTempCleaningEveryNFrames", m_update_temp_cleaning);
m_update_temp_cleaning = startupConfig.GetInt( "UpdateTempCleaningEveryNSeconds", m_update_temp_cleaning);
}
// FIXME: Ultimately this should be in a module.
SendPeriodicAppearanceUpdates = true;
SendPeriodicAppearanceUpdates = false;
IConfig appearanceConfig = m_config.Configs["Appearance"];
if (appearanceConfig != null)
@ -1444,13 +1476,14 @@ namespace OpenSim.Region.Framework.Scenes
if (UpdateOnTimer)
{
m_sceneUpdateTimer = new Timer(MinFrameTime * 1000);
m_sceneUpdateTimer = new Timer(MinFrameTicks);
m_sceneUpdateTimer.AutoReset = true;
m_sceneUpdateTimer.Elapsed += Update;
m_sceneUpdateTimer.Start();
}
else
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Update(-1);
Watchdog.RemoveThread();
m_isRunning = false;
@ -1490,7 +1523,7 @@ namespace OpenSim.Region.Framework.Scenes
public void DoMaintenance(int runs)
{
long? endRun = null;
int runtc;
int runtc, tmpMS;
int previousMaintenanceTick;
if (runs >= 0)
@ -1504,6 +1537,8 @@ namespace OpenSim.Region.Framework.Scenes
runtc = Util.EnvironmentTickCount();
++MaintenanceRun;
// m_log.DebugFormat("[SCENE]: Maintenance run {0} in {1}", MaintenanceRun, Name);
// Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client)
if (MaintenanceRun % (m_update_coarse_locations / 10) == 0)
{
@ -1525,24 +1560,39 @@ namespace OpenSim.Region.Framework.Scenes
}
}
// Delete temp-on-rez stuff
if (MaintenanceRun % m_update_temp_cleaning == 0 && !m_cleaningTemps)
{
// m_log.DebugFormat("[SCENE]: Running temp-on-rez cleaning in {0}", Name);
tmpMS = Util.EnvironmentTickCount();
m_cleaningTemps = true;
Watchdog.RunInThread(
delegate { CleanTempObjects(); m_cleaningTemps = false; },
string.Format("CleanTempObjects ({0})", Name),
null);
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpMS);
}
Watchdog.UpdateThread();
previousMaintenanceTick = m_lastMaintenanceTick;
m_lastMaintenanceTick = Util.EnvironmentTickCount();
runtc = Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, runtc);
runtc = (int)(MinMaintenanceTime * 1000) - runtc;
runtc = MinMaintenanceTicks - runtc;
if (runtc > 0)
Thread.Sleep(runtc);
m_maintenanceWaitEvent.WaitOne(runtc);
// Optionally warn if a frame takes double the amount of time that it should.
if (DebugUpdates
&& Util.EnvironmentTickCountSubtract(
m_lastMaintenanceTick, previousMaintenanceTick) > (int)(MinMaintenanceTime * 1000 * 2))
m_lastMaintenanceTick, previousMaintenanceTick) > MinMaintenanceTicks * 2)
m_log.WarnFormat(
"[SCENE]: Maintenance took {0} ms (desired max {1} ms) in {2}",
Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, previousMaintenanceTick),
MinMaintenanceTime * 1000,
MinMaintenanceTicks,
RegionInfo.RegionName);
}
}
@ -1563,7 +1613,7 @@ namespace OpenSim.Region.Framework.Scenes
// m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName);
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = spareMS = 0;
agentMS = eventMS = backupMS = terrainMS = landMS = spareMS = 0;
try
{
@ -1594,7 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
if (Frame % m_update_physics == 0)
{
if (PhysicsEnabled)
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime);
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameSeconds);
if (SynchronizeScene != null)
SynchronizeScene(this);
@ -1616,21 +1666,7 @@ namespace OpenSim.Region.Framework.Scenes
if (Frame % m_update_presences == 0)
m_sceneGraph.UpdatePresences();
agentMS += Util.EnvironmentTickCountSubtract(tmpMS);
// Delete temp-on-rez stuff
if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps)
{
tmpMS = Util.EnvironmentTickCount();
m_cleaningTemps = true;
Watchdog.RunInThread(
delegate { CleanTempObjects(); m_cleaningTemps = false; },
string.Format("CleanTempObjects ({0})", Name),
null);
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpMS);
}
agentMS += Util.EnvironmentTickCountSubtract(tmpMS);
if (Frame % m_update_events == 0)
{
@ -1700,24 +1736,22 @@ namespace OpenSim.Region.Framework.Scenes
}
EventManager.TriggerRegionHeartbeatEnd(this);
otherMS = tempOnRezMS + eventMS + backupMS + terrainMS + landMS;
otherMS = eventMS + backupMS + terrainMS + landMS;
if (!UpdateOnTimer)
{
Watchdog.UpdateThread();
tmpMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), m_lastFrameTick);
tmpMS = (int)(MinFrameTime * 1000) - tmpMS;
spareMS = MinFrameTicks - Util.EnvironmentTickCountSubtract(m_lastFrameTick);
if (tmpMS > 0)
{
spareMS = tmpMS;
Thread.Sleep(tmpMS);
}
if (spareMS > 0)
m_updateWaitEvent.WaitOne(spareMS);
else
spareMS = 0;
}
else
{
spareMS = Math.Max(0, (int)(MinFrameTime * 1000) - physicsMS2 - agentMS - physicsMS -otherMS);
spareMS = Math.Max(0, MinFrameTicks - physicsMS2 - agentMS - physicsMS - otherMS);
}
previousFrameTick = m_lastFrameTick;
@ -1740,11 +1774,11 @@ namespace OpenSim.Region.Framework.Scenes
// Optionally warn if a frame takes double the amount of time that it should.
if (DebugUpdates
&& Util.EnvironmentTickCountSubtract(
m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2))
m_lastFrameTick, previousFrameTick) > MinFrameTicks * 2)
m_log.WarnFormat(
"[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick),
MinFrameTime * 1000,
MinFrameTicks,
RegionInfo.RegionName);
}
@ -2588,48 +2622,8 @@ namespace OpenSim.Region.Framework.Scenes
return false;
}
// If the user is banned, we won't let any of their objects
// enter. Period.
//
if (RegionInfo.EstateSettings.IsBanned(newObject.OwnerID))
{
m_log.InfoFormat("[INTERREGION]: Denied prim crossing for banned avatar {0}", newObject.OwnerID);
return false;
}
if (newPosition != Vector3.Zero)
newObject.RootPart.GroupPosition = newPosition;
if (!AddSceneObject(newObject))
{
m_log.DebugFormat(
"[INTERREGION]: Problem adding scene object {0} in {1} ", newObject.UUID, RegionInfo.RegionName);
return false;
}
if (!newObject.IsAttachment)
{
// FIXME: It would be better to never add the scene object at all rather than add it and then delete
// it
if (!Permissions.CanObjectEntry(newObject.UUID, true, newObject.AbsolutePosition))
{
// Deny non attachments based on parcel settings
//
m_log.Info("[INTERREGION]: Denied prim crossing because of parcel settings");
DeleteSceneObject(newObject, false);
return false;
}
// For attachments, we need to wait until the agent is root
// before we restart the scripts, or else some functions won't work.
newObject.RootPart.ParentGroup.CreateScriptInstances(0, false, DefaultScriptEngine, GetStateSource(newObject));
newObject.ResumeScripts();
if (newObject.RootPart.KeyframeMotion != null)
newObject.RootPart.KeyframeMotion.UpdateSceneObject(newObject);
}
if (!EntityTransferModule.HandleIncomingSceneObject(newObject, newPosition))
return false;
// Do this as late as possible so that listeners have full access to the incoming object
EventManager.TriggerOnIncomingSceneObject(newObject);
@ -2698,16 +2692,6 @@ namespace OpenSim.Region.Framework.Scenes
return true;
}
private int GetStateSource(SceneObjectGroup sog)
{
ScenePresence sp = GetScenePresence(sog.OwnerID);
if (sp != null)
return sp.GetStateSource();
return 2; // StateSource.PrimCrossing
}
#endregion
#region Add/Remove Avatar Methods
@ -2754,29 +2738,41 @@ namespace OpenSim.Region.Framework.Scenes
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);
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
// We must also set this before adding the client to the client manager so that an exception later on
// does not leave a client manager entry without the scene agent set, which will cause other code
// to fail since any entry in the client manager should have a ScenePresence
//
// XXX: This may be better set for a new client before that client is added to the client manager.
// But need to know what happens in the case where a ScenePresence is already present (and if this
// actually occurs).
client.SceneAgent = sp;
m_clientManager.Add(client);
SubscribeToClientEvents(client);
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
m_eventManager.TriggerOnNewPresence(sp);
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
}
else
{
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
// XXX: This may be better set for a new client before that client is added to the client manager.
// But need to know what happens in the case where a ScenePresence is already present (and if this
// actually occurs).
client.SceneAgent = sp;
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);
reallyNew = false;
}
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
// XXX: This may be better set for a new client before that client is added to the client manager.
// But need to know what happens in the case where a ScenePresence is already present (and if this
// actually occurs).
client.SceneAgent = sp;
}
// This is currently also being done earlier in NewUserConnection for real users to see if this
// resolves problems where HG agents are occasionally seen by others as "Unknown user" in chat and other
@ -4466,14 +4462,9 @@ namespace OpenSim.Region.Framework.Scenes
sp.LifecycleState = ScenePresenceState.Removing;
}
if (sp != null)
{
sp.ControllingClient.Close(force);
return true;
}
sp.ControllingClient.Close(force);
// Agent not here
return false;
return true;
}
/// <summary>

View File

@ -226,7 +226,10 @@ namespace OpenSim.Region.Framework.Scenes
// We must take a copy here since handle is acts like a reference when used in an iterator.
// This leads to race conditions if directly passed to SendCloseChildAgent with more than one neighbour region.
ulong handleCopy = handle;
Util.FireAndForget((o) => { SendCloseChildAgent(agentID, handleCopy, auth_code); });
Util.FireAndForget(
o => SendCloseChildAgent(agentID, handleCopy, auth_code),
null,
"SceneCommunicationService.SendCloseChildAgentConnections");
}
}

View File

@ -943,8 +943,8 @@ namespace OpenSim.Region.Framework.Scenes
{
if (CanBeBackedUp)
{
//m_log.DebugFormat(
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
// m_log.DebugFormat(
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
if (!Backup)
m_scene.EventManager.OnBackup += ProcessBackup;

View File

@ -1589,20 +1589,29 @@ namespace OpenSim.Region.Framework.Scenes
public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
{
byte[] data = new byte[16];
int pos = 0;
byte[] data;
// The flags don't like conversion from uint to byte, so we have to do
// it the crappy way. See the above function :(
if (pTexAnim.Flags == Primitive.TextureAnimMode.ANIM_OFF)
{
data = Utils.EmptyBytes;
}
else
{
data = new byte[16];
int pos = 0;
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
data[pos] = (byte)pTexAnim.Face; pos++;
data[pos] = (byte)pTexAnim.SizeX; pos++;
data[pos] = (byte)pTexAnim.SizeY; pos++;
// The flags don't like conversion from uint to byte, so we have to do
// it the crappy way. See the above function :(
Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
data[pos] = (byte)pTexAnim.Face; pos++;
data[pos] = (byte)pTexAnim.SizeX; pos++;
data[pos] = (byte)pTexAnim.SizeY; pos++;
Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
}
m_TextureAnimation = data;
}

View File

@ -1228,19 +1228,15 @@ namespace OpenSim.Region.Framework.Scenes
// viewers without (e.g. v1 viewers) will not, so we still need to make this call.
if (Scene.AttachmentsModule != null)
{
Util.FireAndForget(o =>
{
Scene.AttachmentsModule.RezAttachments(this);
});
Watchdog.RunJob(
"RezAttachments",
o => Scene.AttachmentsModule.RezAttachments(this),
string.Format("Rez attachments for {0} in {1}", Name, Scene.Name),
null);
}
}
else
{
// We need to restart scripts here so that they receive the correct changed events (CHANGED_TELEPORT
// and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently
// be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are
// not transporting the required data.
//
// We need to restart scripts here so that they receive the correct changed events (CHANGED_TELEPORT
// and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently
// be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are
@ -1254,23 +1250,16 @@ namespace OpenSim.Region.Framework.Scenes
// But XEngine starts all scripts unsuspended. Starting them suspended will not currently work because script rezzing
// is placed in an asynchronous queue in XEngine and so the ResumeScripts() call will almost certainly execute before the
// script is rezzed. This means the ResumeScripts() does absolutely nothing when using XEngine.
//
// One cannot simply iterate over attachments in a fire and forget thread because this would no longer
// be locked, allowing race conditions if other code changes the attachments list.
List<SceneObjectGroup> attachments = GetAttachments();
if (attachments.Count > 0)
{
m_log.DebugFormat(
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
// Resume scripts
foreach (SceneObjectGroup sog in attachments)
{
sog.ScheduleGroupForFullUpdate();
sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
sog.ResumeScripts();
}
Watchdog.RunJob(
"StartAttachmentScripts",
o => RestartAttachmentScripts(attachments),
string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name),
null,
true);
}
}
@ -1294,6 +1283,20 @@ namespace OpenSim.Region.Framework.Scenes
return true;
}
private void RestartAttachmentScripts(List<SceneObjectGroup> attachments)
{
m_log.DebugFormat(
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
// Resume scripts
foreach (SceneObjectGroup sog in attachments)
{
sog.ScheduleGroupForFullUpdate();
sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
sog.ResumeScripts();
}
}
private static bool IsRealLogin(TeleportFlags teleportFlags)
{
return ((teleportFlags & TeleportFlags.ViaLogin) != 0) && ((teleportFlags & TeleportFlags.ViaHGLogin) == 0);
@ -1326,7 +1329,7 @@ namespace OpenSim.Region.Framework.Scenes
UseFakeGroupTitle = false;
SendAvatarDataToAllClients(false);
});
}, null, "Scenepresence.ForceViewersUpdateName");
}
public int GetStateSource()
@ -1809,14 +1812,15 @@ namespace OpenSim.Region.Framework.Scenes
}
// XXX: If we force an update here, then multiple attachments do appear correctly on a destination region
// XXX: If we force an update after activity has completed, then multiple attachments do appear correctly on a destination region
// If we do it a little bit earlier (e.g. when converting the child to a root agent) then this does not work.
// This may be due to viewer code or it may be something we're not doing properly simulator side.
lock (m_attachments)
{
foreach (SceneObjectGroup sog in m_attachments)
sog.ScheduleGroupForFullUpdate();
}
Watchdog.RunJob(
"ScheduleAttachmentsForFullUpdate",
o => ScheduleAttachmentsForFullUpdate(),
string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name),
null,
true);
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
@ -1828,6 +1832,15 @@ namespace OpenSim.Region.Framework.Scenes
}
}
private void ScheduleAttachmentsForFullUpdate()
{
lock (m_attachments)
{
foreach (SceneObjectGroup sog in m_attachments)
sog.ScheduleGroupForFullUpdate();
}
}
/// <summary>
/// Callback for the Camera view block check. Gets called with the results of the camera view block test
/// hitYN is true when there's something in the way.
@ -2618,7 +2631,6 @@ namespace OpenSim.Region.Framework.Scenes
}
}
Vector3 sitPartWorldPosition = part.GetWorldPosition();
ControllingClient.SendClearFollowCamProperties(part.ParentUUID);
ParentID = 0;
@ -2647,13 +2659,13 @@ namespace OpenSim.Region.Framework.Scenes
// Vector3 standPositionAdjustment
// = part.SitTargetPosition + new Vector3(0.5f, 0f, m_sitAvatarHeight / 2f);
Vector3 adjustmentForSitPosition = (part.SitTargetPosition + OffsetPosition) * part.GetWorldRotation();
Vector3 adjustmentForSitPosition = (OffsetPosition - SIT_TARGET_ADJUSTMENT) * part.GetWorldRotation();
// XXX: This is based on the physics capsule sizes. Need to find a better way to read this rather than
// hardcoding here.
Vector3 adjustmentForSitPose = new Vector3(0.74f, 0f, 0f) * standRotation;
Vector3 standPos = sitPartWorldPosition + adjustmentForSitPosition + adjustmentForSitPose;
Vector3 standPos = part.ParentGroup.AbsolutePosition + adjustmentForSitPosition + adjustmentForSitPose;
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Setting stand to pos {0}, (adjustmentForSitPosition {1}, adjustmentForSitPose {2}) rotation {3} for {4} in {5}",
@ -3363,7 +3375,7 @@ namespace OpenSim.Region.Framework.Scenes
SentInitialDataToClient = true;
// Send all scene object to the new client
Watchdog.RunInThread(delegate
Watchdog.RunJob("SendInitialDataToClient", delegate
{
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Sending initial data to {0} agent {1} in {2}, tp flags {3}",
@ -3381,7 +3393,7 @@ namespace OpenSim.Region.Framework.Scenes
if (e != null && e is SceneObjectGroup)
((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient);
}
}, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name), null);
}, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name),null, false, true);
}
/// <summary>
@ -3618,7 +3630,8 @@ namespace OpenSim.Region.Framework.Scenes
agentpos.CopyFrom(cadu, ControllingClient.SessionId);
// Let's get this out of the update loop
Util.FireAndForget(delegate { m_scene.SendOutChildAgentUpdates(agentpos, this); });
Util.FireAndForget(
o => m_scene.SendOutChildAgentUpdates(agentpos, this), null, "ScenePresence.SendOutChildAgentUpdates");
}
}
@ -4036,9 +4049,23 @@ namespace OpenSim.Region.Framework.Scenes
Animator.Animations.SetImplicitDefaultAnimation(cAgent.AnimState.AnimID, cAgent.AnimState.SequenceNum, UUID.Zero);
if (Scene.AttachmentsModule != null)
Scene.AttachmentsModule.CopyAttachments(cAgent, this);
{
// If the JobEngine is running we can schedule this job now and continue rather than waiting for all
// attachments to copy, which might take a long time in the Hypergrid case as the entire inventory
// graph is inspected for each attachments and assets possibly fetched.
//
// We don't need to worry about a race condition as the job to later start the scripts is also
// JobEngine scheduled and so will always occur after this task.
// XXX: This will not be true if JobEngine ever gets more than one thread.
Watchdog.RunJob(
"CopyAttachments",
o => Scene.AttachmentsModule.CopyAttachments(cAgent, this),
string.Format("Copy attachments for {0} entering {1}", Name, Scene.Name),
null,
true);
}
// This must occur after attachments are copied, as it releases the CompleteMovement() calling thread
// This must occur after attachments are copied or scheduled to be copied, as it releases the CompleteMovement() calling thread
// originating from the client completing a teleport. Otherwise, CompleteMovement() code to restart
// script attachments can outrace this thread.
lock (m_originRegionIDAccessLock)
@ -4472,7 +4499,7 @@ namespace OpenSim.Region.Framework.Scenes
}
}
}
});
}, null, "ScenePresence.SendScriptEventToAttachments");
}
/// <summary>

View File

@ -224,7 +224,7 @@ namespace OpenSim.Region.Framework.Scenes
public SimStatsReporter(Scene scene)
{
m_scene = scene;
m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps;
m_reportedFpsCorrectionFactor = scene.MinFrameSeconds * m_nominalReportedFps;
m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
ReportingRegion = scene.RegionInfo;
@ -239,7 +239,7 @@ namespace OpenSim.Region.Framework.Scenes
/// 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);
SlowFramesStatReportThreshold = (int)Math.Ceiling(scene.MinFrameTicks * 1.2);
SlowFramesStat
= new Stat(

View File

@ -35,7 +35,6 @@ 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
{

View File

@ -36,7 +36,6 @@ using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -37,7 +37,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Region.CoreModules.World.Land;
using OpenSim.Region.OptionalModules;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.World.Permissions;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -34,7 +34,6 @@ using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using log4net;
namespace OpenSim.Region.Framework.Scenes.Tests

View File

@ -33,7 +33,6 @@ 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
{

View File

@ -35,7 +35,6 @@ using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -34,7 +34,6 @@ 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
{

View File

@ -34,7 +34,6 @@ 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
{

View File

@ -33,7 +33,6 @@ 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
{

View File

@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -44,7 +44,6 @@ using OpenSim.Region.CoreModules.Framework.EntityTransfer;
using OpenSim.Region.CoreModules.World.Serialiser;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
namespace OpenSim.Region.Framework.Scenes.Tests

View File

@ -43,7 +43,6 @@ using OpenSim.Region.CoreModules.World.Serialiser;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Region.Physics.Manager;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

View File

@ -47,7 +47,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
namespace OpenSim.Region.Framework.Scenes.Tests

View File

@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.Framework.EntityTransfer;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
using OpenSim.Region.CoreModules.World.Permissions;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.Framework.Scenes.Tests
{

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