Compare commits

...

80 Commits

Author SHA1 Message Date
Diva Canto 47ce1a47bb Deleted all [ZZZ] debug messages. 2013-07-24 14:24:17 -07:00
Diva Canto 10def95c27 The previous commit did fix the infinity problem! I'm putting the same time on TP_V1 and adding a big red warning on top of those lines. 2013-07-24 14:22:59 -07:00
Diva Canto dab01a10d9 Today's wild shot at the infinity problem. Wait on the child agent left behind. 2013-07-24 12:54:13 -07:00
Diva Canto b44d96618c Amend previous commit. 2013-07-24 11:42:35 -07:00
Diva Canto 3a6c690e69 Added config var that we all thought was already there: see_into_region. (Note: different from the defunct see_into_neighboring_sim, which used to control the process from the other end). This enables child agents in neighbors for which the root agent doesn't have permission to be in. 2013-07-24 11:23:19 -07:00
Diva Canto 051cf81c9f Decreased the time of group cache to 1 min, because it was getting on my nerves that it takes so long to let go of old info. 2013-07-24 11:13:31 -07:00
Diva Canto c7a3714c37 Don't touch the Current Outfit folder also on coming back home 2013-07-23 15:04:24 -07:00
Diva Canto 1815c2f98c Restoring landing on prims, which had been affected by the edit beams commit. 2013-07-23 14:23:22 -07:00
Diva Canto 7c884d16f4 In renaming the folders for hypergriding, don't rename the Current Outfit folder. The viewer doesn't like that. 2013-07-23 14:17:32 -07:00
Diva Canto f3d31e6e53 Improvement of fetching name in groups 2013-07-23 11:29:53 -07:00
Diva Canto 701e15760c That didn't change anything.
Revert "One more tweak to check the infinity problem"

This reverts commit 2b696bc0c8.
2013-07-23 10:10:33 -07:00
Diva Canto 2b696bc0c8 One more tweak to check the infinity problem 2013-07-23 10:04:08 -07:00
Justin Clark-Casey (justincc) 9e277b54b9 Improve spacing between data and units on console stats display 2013-07-23 17:48:35 +01:00
Justin Clark-Casey (justincc) 805ac6435e For stats which can show average change over time, show the last sample as well as the average.
This is somewhat cryptic at the moment, need to improve documentation.
2013-07-23 17:48:10 +01:00
Justin Clark-Casey (justincc) 511801c607 Add proper method doc and comments to m_dataPresentEvent (from d9d9959) 2013-07-23 17:48:05 +01:00
Diva Canto ff294dce6c Moved TriggerOnMakeRootAgent back to the end of MakeRootAgent, to see if that eliminates the temporary placement at infinity upon TPs 2013-07-22 22:00:20 -07:00
Diva Canto 9c1ec98415 When LLSD handlers are not found, the server should return a 404 instead of a valid response saying 'No handler registered for LLSD Requests' 2013-07-22 21:45:57 -07:00
Diva Canto 01561166aa One more thing to test in order to let CompleteMovement go up the stack. 2013-07-22 20:49:40 -07:00
Diva Canto 8d1108e542 Minor adjustment on timings of waits. 2013-07-22 20:20:48 -07:00
Diva Canto be4034fd1c Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-22 18:47:10 -07:00
Diva Canto 87a19d4cab Changed the RegionHandshake packet to the Unknown queue, so that it is sent with high priority and hopefully gets to the client before AgentMovementComplete 2013-07-22 18:46:51 -07:00
Justin Clark-Casey (justincc) 35e46c2836 Add clientstack.InboxPacketsCount stat. This records the number of packets waiting to be processed at the second stage (after initial UDP processing)
If this consistently increases then this is a problem since it means the simulator is receiving more requests than it can distribute to other parts of the code.
2013-07-23 00:31:57 +01:00
Justin Clark-Casey (justincc) 0d90834f86 Add clientstack.OutgoingUDPSendsCount stat to show number of outbound UDP packets sent by a region per second 2013-07-23 00:15:58 +01:00
Justin Clark-Casey (justincc) 0755fc2f65 Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-22 23:59:10 +01:00
Justin Clark-Casey (justincc) 94072fb633 Record raw number of UDP receives as clientstack.IncomingUDPReceivesCount 2013-07-22 23:58:45 +01:00
Robert Adams 882efc1a2e Added check for user movement specification before discarding an incoming
AgentUpdate packet. This fixes the problem with vehicles not moving forward
after the first up-arrow.
Code to fix a potential exception when using different IClientAPIs.

Conflicts:

	OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
2013-07-22 15:51:44 -07:00
Justin Clark-Casey (justincc) deace1f344 Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-22 23:37:55 +01:00
Justin Clark-Casey (justincc) 7f0f3cc011 Add AverageUDPProcessTime stat to try and get a handle on how long we're taking on the initial processing of a UDP packet.
If we're not receiving packets with multiple threads (m_asyncPacketHandling) then this is critical since it will limit the number of incoming UDP requests that the region can handle and affects packet loss.
If m_asyncPacketHandling then this is less critical though a long process will increase the scope for threads to race.
This is an experimental stat which may be changed.
2013-07-22 23:30:09 +01:00
Diva Canto c163276032 This commit message intentionally left blank (last commit was idiotic) 2013-07-22 14:35:14 -07:00
Diva Canto a2a05d470e Improve the opening test in CompleteMovement, to account for multiple flags besides ViaLogin. 2013-07-22 14:23:50 -07:00
Diva Canto d5f8b7924c More on putting TP V1 as it was 2013-07-22 13:29:58 -07:00
Diva Canto d70a0c09cb Minor improvements on TP V1 trying to make it exactly as it was before. 2013-07-22 13:16:13 -07:00
Diva Canto df09fdf65d New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 11:54:35 -07:00
Diva Canto 37337a4de9 Further tweaks on TPs: not sending the callback URL and instead waiting 15sec before closing the agent. This seems to be working fairly well. The viewer seems to have an 8 sec delay between UseCircuitCode and CompleteMovement.
Also added back the position on UpdateAgent, because it's needed for TPing between neighboring regions.
2013-07-21 20:22:13 -07:00
Diva Canto 8d98d287bd EXPERIMENTAL: yet another variation of ES/EAC/TPFinish 2013-07-21 17:38:01 -07:00
Diva Canto e1404adac6 That also didn't work.
Revert "One last thing before I stop on this: now not sending ES and EAC to the viewer. Looking in GridProxy, I don't see those messages in the Linden grid."

This reverts commit 1d27c9f4d1.
2013-07-21 17:23:52 -07:00
Diva Canto 1d27c9f4d1 One last thing before I stop on this: now not sending ES and EAC to the viewer. Looking in GridProxy, I don't see those messages in the Linden grid. 2013-07-21 17:18:54 -07:00
Diva Canto 428b51ffda That also didn't work.
Revert "EXPERIMENTAL: Trying the other order -- delaying MoveAgentIntoRegion until after the departing agent is released."

This reverts commit fe2487f8d3.
2013-07-21 17:11:25 -07:00
Diva Canto fe2487f8d3 EXPERIMENTAL: Trying the other order -- delaying MoveAgentIntoRegion until after the departing agent is released. 2013-07-21 16:49:24 -07:00
Diva Canto b69ddbb66b That didn't work. If anything, it made it worse.
Revert "EXPERIMENTAL: Switch MoveAgentIntoRegion with MakeRootAgent, to see if that eliminates the momentary position at infinity."

This reverts commit 1cb5e31716.
2013-07-21 16:18:24 -07:00
Diva Canto 1cb5e31716 EXPERIMENTAL: Switch MoveAgentIntoRegion with MakeRootAgent, to see if that eliminates the momentary position at infinity. 2013-07-21 16:06:03 -07:00
Diva Canto 7f60800ca6 Don't post Link asset types back to the home grid 2013-07-21 15:46:00 -07:00
Diva Canto ceaa7e9a54 Better version of previous commit 2013-07-21 14:39:50 -07:00
Diva Canto cab3e9978b Add the Current Outfit folder as an available folder in the SuitcaseInventory. 2013-07-21 14:34:43 -07:00
Diva Canto 049022717c Revert "WARNING: DON'T USE THIS COMMIT -- major spew on HG teleports."
This reverts commit 40c54a718f.
2013-07-21 14:08:22 -07:00
Diva Canto 40c54a718f WARNING: DON'T USE THIS COMMIT -- major spew on HG teleports. 2013-07-21 13:46:03 -07:00
Diva Canto 315097b8b9 Minor fixes 2013-07-21 13:36:03 -07:00
Diva Canto 5e5aa5fba7 Now diagnosing a strange behavior where upon TP agents are temporarily placed in very large coordinates, then snap back.
Extra checks in preparation for dropping obsolete data in agent transfers.
2013-07-21 12:44:46 -07:00
Diva Canto 9801d0d4c8 Minor aesthetic change to make things more clear. 2013-07-21 08:50:52 -07:00
Diva Canto d95a470442 The quaternion delta was a bit to high, now that the head rotation is out of the equation. (head rotation was the problematic one) 2013-07-20 19:20:20 -07:00
Diva Canto 74a341fd22 EDIT BEAMS!!! They had been missing from OpenSim since ever. Thanks to lkalif for telling me how to route the information. The viewer effect is under the distance filter, so only avatars with cameras < 10m away see the beams. 2013-07-20 17:58:32 -07:00
Diva Canto 95b248e9e5 Filter certain viewer effects depending on distance between the avatar that is generating the effect and the cameras of the observers. In particular, this applies to LookAt (which is really verbose and occurs every time users move the mouse) and Beam (which doesn't occur that often, but that can be extremely noisy (10.sec) when it happens) 2013-07-20 15:42:01 -07:00
Diva Canto 340abd1110 A couple of small optimizations over the previous commit 2013-07-20 13:42:39 -07:00
Diva Canto 0910c5c101 Manage AgentUpdates more sanely:
- The existing event to scene has been split into 2: OnAgentUpdate and OnAgentCameraUpdate, to better reflect the two types of updates that the viewer sends. We can run one without the other, which is what happens when the avie is still but the user is camming around
- Added thresholds (as opposed to equality) to determine whether the update is significant or not. I thin these thresholds are ok, but we can play with them later
- Ignore updates of HeadRotation, which were problematic and aren't being used up stream
2013-07-20 12:20:35 -07:00
Diva Canto f0126a1575 Fixed the stats in show client stats. Also left some comments with observations about AgentUpdates. 2013-07-19 22:11:32 -07:00
Diva Canto 0d5b2dd5ce Fix HGTravelStore.migrations in SQLite (mantis #6709) 2013-07-19 20:24:56 -07:00
Diva Canto 64cda1b26e Removed verbose debug from previous commit 2013-07-19 13:19:36 -07:00
Diva Canto 778babaab2 Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-19 13:18:04 -07:00
Diva Canto d30e5f7ded PollServiceRequestManager: changed the long poll from a Queue to a List. No need to dequeue and enqueue items every 1sec. 2013-07-19 13:17:15 -07:00
Robert Adams f05654d8d6 BulletSim: add position resetting for stationary avatars so they don't
move around when standing on a stationary object.
Create proper linkage between BSCharacter and its actor by generating
a UpdatedProperties event the same way BSPrim does.
2013-07-18 19:19:29 -07:00
Diva Canto 52bb732692 Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-18 19:02:46 -07:00
Diva Canto dc88ffc5b4 Delay the enqueueing of non-longpoll requests for 100ms. No need to have these requests actively on the processing queue if it seems they're not ready. 2013-07-18 17:17:20 -07:00
Justin Clark-Casey (justincc) 07420a3b4d furhter shorten CheckAgentUpdateSignificance(). No real perf impact. 2013-07-19 01:00:38 +01:00
Justin Clark-Casey (justincc) 42e5856464 Remove some pointless code in CheckAgentUpdateSignificance() 2013-07-19 00:56:45 +01:00
Justin Clark-Casey (justincc) 3b8e7ff013 Make the check as to whether any particular inbound AgentUpdate packet is significant much earlier in UDP processing (i.e. before we pointlessly place such packets on internal queues, etc.)
Appears to have some impact on cpu but needs testing.
2013-07-19 00:51:13 +01:00
Justin Clark-Casey (justincc) 5c74f3ec9c Add measure of number of inbound AgentUpdates that were seen as significant to "show client stats" (i.e. sent on for further processing instead of being discarded)
Added here since it was the most convenient place
Number is in the last column, "Sig. AgentUpdates" along with percentage of all AgentUpdates
Percentage largely falls over time, most cpu for processing AgentUpdates may be in UDP processing as turning this off even earlier (with "debug lludp toggle agentupdate" results in a big cpu fall
Also tidies up display.
2013-07-19 00:16:09 +01:00
Diva Canto d1e9beead8 Revert "Now trying DoubleQueue instead of BlockingQueue for the PollServiceRequestManager."
This reverts commit 5f95f4d78e.
2013-07-18 15:52:07 -07:00
Justin Clark-Casey (justincc) 35aa6c86fe Hack in console command "debug lludp toggle agentupdate" to allow AgentUpdate in packets to be discarded at a very early stage.
Enabling this will stop anybody from moving on a sim, though all other updates should be unaffected.
Appears to make some cpu difference on very basic testing with a static standing avatar (though not all that much).
Need to see the results with much higher av numbers.
2013-07-18 23:05:45 +01:00
Justin Clark-Casey (justincc) 5cdc21aac7 minor: provide user feedback in the log for now when udp in/out bound threads are started/stopped 2013-07-18 22:54:10 +01:00
Justin Clark-Casey (justincc) cbb47f8489 Merge branch 'cpu-performance' of ssh://opensimulator.org/var/git/opensim into cpu-performance 2013-07-18 22:43:15 +01:00
Justin Clark-Casey (justincc) b2b29b7ec0 Fix up a temporary debugging change from last commit which stopped "lludp stop out" from actually doing anything 2013-07-18 22:42:25 +01:00
Diva Canto 27377194cd Changed the timoeut of EQ 502s (no events) to 50 secs. The viewer post requests timeout in 60 secs.
There's plenty of room for improvement in handling the EQs. Some other time...
2013-07-18 13:48:56 -07:00
Justin Clark-Casey (justincc) 8c6761c152 Do some simple queue empty checks in the main outgoing udp loop instead of always performing these on a separate fired thread.
This appears to improve cpu usage since launching a new thread is more expensive than performing a small amount of inline logic.
However, needs testing at scale.
2013-07-18 21:28:36 +01:00
Diva Canto 553d9cc5d2 Applying the same fix here that dan lake applied to master -- unfortunately I can't cherry-pick because that commit has 2 parents... 2013-07-18 07:52:14 -07:00
Diva Canto c685cc1799 Revert "This is a completely unreasonable thing to do, effectively defying the purpose of BlockingQueues. Trying this, to see the effect on CPU."
This reverts commit 5232ab0496.
2013-07-17 20:42:38 -07:00
Justin Clark-Casey (justincc) 1ba5a05cf7 try Hacking in an AutoResetEvent to control the outgoing UDP loop instead of a continuous loop with sleeps.
Does appear to have a cpu impact but may need further tweaking
2013-07-18 01:17:46 +01:00
Justin Clark-Casey (justincc) 0af3b5ed9a Revert "Put in temporary hack for performnace 'queue-empty' logic on a persistent thread rather than through fire and forget"
This reverts commit b402220dbb.

Eliminating fire and forget here does not appear to make a significant difference.
2013-07-18 00:51:10 +01:00
Justin Clark-Casey (justincc) a94a43d249 Revert "Properly remove the hack queue update thread when the voewr shuts down"
This reverts commit 7c544c0d4e.
2013-07-18 00:50:16 +01:00
Justin Clark-Casey (justincc) 7c544c0d4e Properly remove the hack queue update thread when the voewr shuts down
No functional change.
2013-07-18 00:39:28 +01:00
Justin Clark-Casey (justincc) b402220dbb Put in temporary hack for performnace 'queue-empty' logic on a persistent thread rather than through fire and forget
May not scale since this gives each client its own thread.
2013-07-18 00:30:22 +01:00
34 changed files with 1108 additions and 378 deletions

View File

@ -1402,17 +1402,16 @@ namespace OpenSim.Groups
if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
// TODO: All the client update functions need to be reexamined because most do too much and send too much stuff // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID); string firstname = "Unknown", lastname = "Unknown";
string firstname, lastname; string name = m_UserManagement.GetUserName(dataForAgentID);
if (account != null) if (!string.IsNullOrEmpty(name))
{ {
firstname = account.FirstName; string[] parts = name.Split(new char[] { ' ' });
lastname = account.LastName; if (parts.Length >= 2)
{
firstname = parts[0];
lastname = parts[1];
} }
else
{
firstname = "Unknown";
lastname = "Unknown";
} }
remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,

View File

@ -53,7 +53,7 @@ namespace OpenSim.Groups
private ForeignImporter m_ForeignImporter; private ForeignImporter m_ForeignImporter;
private Dictionary<string, bool> m_ActiveRequests = new Dictionary<string, bool>(); private Dictionary<string, bool> m_ActiveRequests = new Dictionary<string, bool>();
private const int GROUPS_CACHE_TIMEOUT = 5 * 60; // 5 minutes private const int GROUPS_CACHE_TIMEOUT = 1 * 60; // 1 minutes
// This all important cache cahces objects of different types: // This all important cache cahces objects of different types:
// group-<GroupID> or group-<Name> => ExtendedGroupRecord // group-<GroupID> or group-<Name> => ExtendedGroupRecord

View File

@ -1,18 +1,18 @@
:VERSION 1 # -------------------------- :VERSION 2 # --------------------------
BEGIN; BEGIN;
CREATE TABLE hg_traveling_data( CREATE TABLE hg_traveling_data(
SessionID VARCHAR(36) NOT NULL, SessionID VARCHAR(36) NOT NULL,
UserID VARCHAR(36) NOT NULL, UserID VARCHAR(36) NOT NULL,
GridExternalName VARCHAR(255) NOT NULL DEFAULT '', GridExternalName VARCHAR(255) NOT NULL DEFAULT "",
ServiceToken VARCHAR(255) NOT NULL DEFAULT '', ServiceToken VARCHAR(255) NOT NULL DEFAULT "",
ClientIPAddress VARCHAR(16) NOT NULL DEFAULT '', ClientIPAddress VARCHAR(16) NOT NULL DEFAULT "",
MyIPAddress VARCHAR(16) NOT NULL DEFAULT '', MyIPAddress VARCHAR(16) NOT NULL DEFAULT "",
TMStamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, TMStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`SessionID`), PRIMARY KEY(SessionID),
KEY (`UserID`) UNIQUE(UserID)
) ENGINE=InnoDB; );
COMMIT; COMMIT;

View File

@ -287,7 +287,7 @@ namespace OpenSim.Framework
public Vector3 AtAxis; public Vector3 AtAxis;
public Vector3 LeftAxis; public Vector3 LeftAxis;
public Vector3 UpAxis; public Vector3 UpAxis;
public bool ChangedGrid; public bool SenderWantsToWaitForRoot;
public float Far; public float Far;
public float Aspect; public float Aspect;
@ -356,8 +356,9 @@ namespace OpenSim.Framework
args["left_axis"] = OSD.FromString(LeftAxis.ToString()); args["left_axis"] = OSD.FromString(LeftAxis.ToString());
args["up_axis"] = OSD.FromString(UpAxis.ToString()); args["up_axis"] = OSD.FromString(UpAxis.ToString());
//backwards compatibility
args["changed_grid"] = OSD.FromBoolean(ChangedGrid); args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot);
args["far"] = OSD.FromReal(Far); args["far"] = OSD.FromReal(Far);
args["aspect"] = OSD.FromReal(Aspect); args["aspect"] = OSD.FromReal(Aspect);
@ -526,8 +527,8 @@ namespace OpenSim.Framework
if (args["up_axis"] != null) if (args["up_axis"] != null)
Vector3.TryParse(args["up_axis"].AsString(), out AtAxis); Vector3.TryParse(args["up_axis"].AsString(), out AtAxis);
if (args["changed_grid"] != null) if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null)
ChangedGrid = args["changed_grid"].AsBoolean(); SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean();
if (args["far"] != null) if (args["far"] != null)
Far = (float)(args["far"].AsReal()); Far = (float)(args["far"].AsReal());
@ -634,17 +635,17 @@ namespace OpenSim.Framework
// The code to unpack textures, visuals, wearables and attachments // The code to unpack textures, visuals, wearables and attachments
// should be removed; packed appearance contains the full appearance // should be removed; packed appearance contains the full appearance
// This is retained for backward compatibility only // This is retained for backward compatibility only
if (args["texture_entry"] != null) if (args.ContainsKey("texture_entry") && args["texture_entry"] != null)
{ {
byte[] rawtextures = args["texture_entry"].AsBinary(); byte[] rawtextures = args["texture_entry"].AsBinary();
Primitive.TextureEntry textures = new Primitive.TextureEntry(rawtextures,0,rawtextures.Length); Primitive.TextureEntry textures = new Primitive.TextureEntry(rawtextures,0,rawtextures.Length);
Appearance.SetTextureEntries(textures); Appearance.SetTextureEntries(textures);
} }
if (args["visual_params"] != null) if (args.ContainsKey("visual_params") && args["visual_params"] != null)
Appearance.SetVisualParams(args["visual_params"].AsBinary()); Appearance.SetVisualParams(args["visual_params"].AsBinary());
if ((args["wearables"] != null) && (args["wearables"]).Type == OSDType.Array) if (args.ContainsKey("wearables") && (args["wearables"] != null) && (args["wearables"]).Type == OSDType.Array)
{ {
OSDArray wears = (OSDArray)(args["wearables"]); OSDArray wears = (OSDArray)(args["wearables"]);
for (int i = 0; i < wears.Count / 2; i++) for (int i = 0; i < wears.Count / 2; i++)
@ -654,7 +655,7 @@ namespace OpenSim.Framework
} }
} }
if ((args["attachments"] != null) && (args["attachments"]).Type == OSDType.Array) if (args.ContainsKey("attachments") && (args["attachments"] != null) && (args["attachments"]).Type == OSDType.Array)
{ {
OSDArray attachs = (OSDArray)(args["attachments"]); OSDArray attachs = (OSDArray)(args["attachments"]);
foreach (OSD o in attachs) foreach (OSD o in attachs)

View File

@ -825,6 +825,8 @@ namespace OpenSim.Framework
/// </remarks> /// </remarks>
event UpdateAgent OnAgentUpdate; event UpdateAgent OnAgentUpdate;
event UpdateAgent OnAgentCameraUpdate;
event AgentRequestSit OnAgentRequestSit; event AgentRequestSit OnAgentRequestSit;
event AgentSit OnAgentSit; event AgentSit OnAgentSit;
event AvatarPickerRequest OnAvatarPickerRequest; event AvatarPickerRequest OnAvatarPickerRequest;
@ -1474,7 +1476,7 @@ namespace OpenSim.Framework
void SendChangeUserRights(UUID agentID, UUID friendID, int rights); void SendChangeUserRights(UUID agentID, UUID friendID, int rights);
void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId); void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId);
void StopFlying(ISceneEntity presence); void SendAgentTerseUpdate(ISceneEntity presence);
void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data); void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data);
} }

View File

@ -225,7 +225,13 @@ namespace OpenSim.Framework.Monitoring
public virtual string ToConsoleString() public virtual string ToConsoleString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName); sb.AppendFormat(
"{0}.{1}.{2} : {3}{4}",
Category,
Container,
ShortName,
Value,
UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName));
AppendMeasuresOfInterest(sb); AppendMeasuresOfInterest(sb);
@ -253,6 +259,8 @@ namespace OpenSim.Framework.Monitoring
== MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime)
{ {
double totalChange = 0; double totalChange = 0;
double lastChangeOverTime = 0;
double? penultimateSample = null;
double? lastSample = null; double? lastSample = null;
lock (m_samples) lock (m_samples)
@ -266,13 +274,24 @@ namespace OpenSim.Framework.Monitoring
if (lastSample != null) if (lastSample != null)
totalChange += s - (double)lastSample; totalChange += s - (double)lastSample;
penultimateSample = lastSample;
lastSample = s; lastSample = s;
} }
} }
if (lastSample != null && penultimateSample != null)
lastChangeOverTime = (double)lastSample - (double)penultimateSample;
int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1;
sb.AppendFormat(", {0:0.##} {1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName); double averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000);
sb.AppendFormat(
", {0:0.##}{1}/s, {2:0.##}{3}/s",
lastChangeOverTime,
UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName),
averageChangeOverTime,
UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName));
} }
} }
} }

View File

@ -1189,6 +1189,7 @@ namespace OpenSim.Framework.Servers.HttpServer
OSD llsdResponse = null; OSD llsdResponse = null;
bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest")); bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest"));
bool nohandler = false;
if (requestBody.Length == 0) if (requestBody.Length == 0)
// Get Request // Get Request
@ -1227,17 +1228,19 @@ namespace OpenSim.Framework.Servers.HttpServer
{ {
// Oops, no handler for this.. give em the failed message // Oops, no handler for this.. give em the failed message
llsdResponse = GenerateNoLLSDHandlerResponse(); llsdResponse = GenerateNoLLSDHandlerResponse();
nohandler = true;
} }
} }
} }
else else
{ {
llsdResponse = GenerateNoLLSDHandlerResponse(); llsdResponse = GenerateNoLLSDHandlerResponse();
nohandler = true;
} }
byte[] buffer = new byte[0]; byte[] buffer = new byte[0];
if (llsdResponse.ToString() == "shutdown404!") if (llsdResponse.ToString() == "shutdown404!" || nohandler)
{ {
response.ContentType = "text/plain"; response.ContentType = "text/plain";
response.StatusCode = 404; response.StatusCode = 404;

View File

@ -47,7 +47,7 @@ namespace OpenSim.Framework.Servers.HttpServer
private readonly BaseHttpServer m_server; private readonly BaseHttpServer m_server;
private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>(); private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>();
private static Queue<PollServiceHttpRequest> m_longPollRequests = new Queue<PollServiceHttpRequest>(); private static List<PollServiceHttpRequest> m_longPollRequests = new List<PollServiceHttpRequest>();
private uint m_WorkerThreadCount = 0; private uint m_WorkerThreadCount = 0;
private Thread[] m_workerThreads; private Thread[] m_workerThreads;
@ -96,7 +96,17 @@ namespace OpenSim.Framework.Servers.HttpServer
private void ReQueueEvent(PollServiceHttpRequest req) private void ReQueueEvent(PollServiceHttpRequest req)
{ {
if (m_running) if (m_running)
{
// delay the enqueueing for 100ms. There's no need to have the event
// actively on the queue
Timer t = new Timer(self => {
((Timer)self).Dispose();
m_requests.Enqueue(req); m_requests.Enqueue(req);
});
t.Change(100, Timeout.Infinite);
}
} }
public void Enqueue(PollServiceHttpRequest req) public void Enqueue(PollServiceHttpRequest req)
@ -106,7 +116,7 @@ namespace OpenSim.Framework.Servers.HttpServer
if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll)
{ {
lock (m_longPollRequests) lock (m_longPollRequests)
m_longPollRequests.Enqueue(req); m_longPollRequests.Add(req);
} }
else else
m_requests.Enqueue(req); m_requests.Enqueue(req);
@ -118,7 +128,7 @@ namespace OpenSim.Framework.Servers.HttpServer
// The only purpose of this thread is to check the EQs for events. // The only purpose of this thread is to check the EQs for events.
// If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests. // If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests.
// If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests. // If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests.
// All other types of tasks (Inventory handlers) don't have the long-poll nature, // All other types of tasks (Inventory handlers, http-in, etc) don't have the long-poll nature,
// so if they aren't ready to be served by a worker thread (no events), they are placed // so if they aren't ready to be served by a worker thread (no events), they are placed
// directly back in the "ready-to-serve" queue by the worker thread. // directly back in the "ready-to-serve" queue by the worker thread.
while (m_running) while (m_running)
@ -129,18 +139,20 @@ namespace OpenSim.Framework.Servers.HttpServer
List<PollServiceHttpRequest> not_ready = new List<PollServiceHttpRequest>(); List<PollServiceHttpRequest> not_ready = new List<PollServiceHttpRequest>();
lock (m_longPollRequests) lock (m_longPollRequests)
{ {
while (m_longPollRequests.Count > 0 && m_running) if (m_longPollRequests.Count > 0 && m_running)
{ {
PollServiceHttpRequest req = m_longPollRequests.Dequeue(); List<PollServiceHttpRequest> ready = m_longPollRequests.FindAll(req =>
if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ
(Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout
m_requests.Enqueue(req); );
else
not_ready.Add(req);
}
foreach (PollServiceHttpRequest req in not_ready) ready.ForEach(req =>
m_longPollRequests.Enqueue(req); {
m_requests.Enqueue(req);
m_longPollRequests.Remove(req);
});
}
} }
} }
@ -159,8 +171,8 @@ namespace OpenSim.Framework.Servers.HttpServer
lock (m_longPollRequests) lock (m_longPollRequests)
{ {
while (m_longPollRequests.Count > 0 && m_running) if (m_longPollRequests.Count > 0 && m_running)
m_requests.Enqueue(m_longPollRequests.Dequeue()); m_longPollRequests.ForEach(req => m_requests.Enqueue(req));
} }
while (m_requests.Count() > 0) while (m_requests.Count() > 0)
@ -176,6 +188,7 @@ namespace OpenSim.Framework.Servers.HttpServer
} }
} }
m_longPollRequests.Clear();
m_requests.Clear(); m_requests.Clear();
} }
@ -185,21 +198,10 @@ namespace OpenSim.Framework.Servers.HttpServer
{ {
while (m_running) while (m_running)
{ {
Watchdog.UpdateThread(); PollServiceHttpRequest req = m_requests.Dequeue(5000);
PollServiceHttpRequest req = null;
lock (m_requests)
{
if (m_requests.Count() > 0)
req = m_requests.Dequeue();
}
if (req == null)
Thread.Sleep(100);
else
{
//PollServiceHttpRequest req = m_requests.Dequeue(5000);
//m_log.WarnFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString())); //m_log.WarnFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString()));
Watchdog.UpdateThread();
if (req != null) if (req != null)
{ {
try try
@ -261,4 +263,3 @@ namespace OpenSim.Framework.Servers.HttpServer
} }
} }
} }
}

View File

@ -1779,10 +1779,12 @@ namespace OpenSim.Framework
FireAndForget(callback, null); FireAndForget(callback, null);
} }
public static void InitThreadPool(int maxThreads) public static void InitThreadPool(int minThreads, int maxThreads)
{ {
if (maxThreads < 2) if (maxThreads < 2)
throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2");
if (minThreads > maxThreads || minThreads < 2)
throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads");
if (m_ThreadPool != null) if (m_ThreadPool != null)
throw new InvalidOperationException("SmartThreadPool is already initialized"); throw new InvalidOperationException("SmartThreadPool is already initialized");
@ -1791,6 +1793,7 @@ namespace OpenSim.Framework
startInfo.IdleTimeout = 2000; startInfo.IdleTimeout = 2000;
startInfo.MaxWorkerThreads = maxThreads; startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = 2; startInfo.MinWorkerThreads = 2;
startInfo.MinWorkerThreads = minThreads;
m_ThreadPool = new SmartThreadPool(startInfo); m_ThreadPool = new SmartThreadPool(startInfo);
} }
@ -1865,7 +1868,7 @@ namespace OpenSim.Framework
break; break;
case FireAndForgetMethod.SmartThreadPool: case FireAndForgetMethod.SmartThreadPool:
if (m_ThreadPool == null) if (m_ThreadPool == null)
InitThreadPool(15); InitThreadPool(2, 15);
m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
break; break;
case FireAndForgetMethod.Thread: case FireAndForgetMethod.Thread:

View File

@ -86,6 +86,7 @@ namespace OpenSim
IConfig startupConfig = Config.Configs["Startup"]; IConfig startupConfig = Config.Configs["Startup"];
IConfig networkConfig = Config.Configs["Network"]; IConfig networkConfig = Config.Configs["Network"];
int stpMinThreads = 2;
int stpMaxThreads = 15; int stpMaxThreads = 15;
if (startupConfig != null) if (startupConfig != null)
@ -112,12 +113,13 @@ namespace OpenSim
if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse<FireAndForgetMethod>(asyncCallMethodStr, out asyncCallMethod)) if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse<FireAndForgetMethod>(asyncCallMethodStr, out asyncCallMethod))
Util.FireAndForgetMethod = asyncCallMethod; Util.FireAndForgetMethod = asyncCallMethod;
stpMinThreads = startupConfig.GetInt("MinPoolThreads", 15);
stpMaxThreads = startupConfig.GetInt("MaxPoolThreads", 15); stpMaxThreads = startupConfig.GetInt("MaxPoolThreads", 15);
m_consolePrompt = startupConfig.GetString("ConsolePrompt", @"Region (\R) "); m_consolePrompt = startupConfig.GetString("ConsolePrompt", @"Region (\R) ");
} }
if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
Util.InitThreadPool(stpMaxThreads); Util.InitThreadPool(stpMinThreads, stpMaxThreads);
m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod); m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod);
} }

View File

@ -65,6 +65,13 @@ namespace OpenSim.Region.ClientStack.Linden
/// </value> /// </value>
public int DebugLevel { get; set; } public int DebugLevel { get; set; }
// Viewer post requests timeout in 60 secs
// https://bitbucket.org/lindenlab/viewer-release/src/421c20423df93d650cc305dc115922bb30040999/indra/llmessage/llhttpclient.cpp?at=default#cl-44
//
private const int VIEWER_TIMEOUT = 60 * 1000;
// Just to be safe, we work on a 10 sec shorter cycle
private const int SERVER_EQ_TIME_NO_EVENTS = VIEWER_TIMEOUT - (10 * 1000);
protected Scene m_scene; protected Scene m_scene;
private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>(); private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>();
@ -364,7 +371,7 @@ namespace OpenSim.Region.ClientStack.Linden
caps.RegisterPollHandler( caps.RegisterPollHandler(
"EventQueueGet", "EventQueueGet",
new PollServiceEventArgs(null, GenerateEqgCapPath(eventQueueGetUUID), HasEvents, GetEvents, NoEvents, agentID, 40000)); new PollServiceEventArgs(null, GenerateEqgCapPath(eventQueueGetUUID), HasEvents, GetEvents, NoEvents, agentID, SERVER_EQ_TIME_NO_EVENTS));
Random rnd = new Random(Environment.TickCount); Random rnd = new Random(Environment.TickCount);
lock (m_ids) lock (m_ids)

View File

@ -96,6 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public event Action<IClientAPI, bool> OnCompleteMovementToRegion; public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnPreAgentUpdate;
public event UpdateAgent OnAgentUpdate; public event UpdateAgent OnAgentUpdate;
public event UpdateAgent OnAgentCameraUpdate;
public event AgentRequestSit OnAgentRequestSit; public event AgentRequestSit OnAgentRequestSit;
public event AgentSit OnAgentSit; public event AgentSit OnAgentSit;
public event AvatarPickerRequest OnAvatarPickerRequest; public event AvatarPickerRequest OnAvatarPickerRequest;
@ -357,7 +358,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// This does mean that agent updates must be processed synchronously, at least for each client, and called methods /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods
/// cannot retain a reference to it outside of that method. /// cannot retain a reference to it outside of that method.
/// </remarks> /// </remarks>
private AgentUpdateArgs m_lastAgentUpdateArgs; private AgentUpdateArgs m_thisAgentUpdateArgs = new AgentUpdateArgs();
protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@ -485,6 +486,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_udpServer = udpServer; m_udpServer = udpServer;
m_udpClient = udpClient; m_udpClient = udpClient;
m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnQueueEmpty += HandleQueueEmpty;
m_udpClient.HasUpdates += HandleHasUpdates;
m_udpClient.OnPacketStats += PopulateStats; m_udpClient.OnPacketStats += PopulateStats;
m_prioritizer = new Prioritizer(m_scene); m_prioritizer = new Prioritizer(m_scene);
@ -809,7 +811,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
handshake.RegionInfo4[0].RegionFlagsExtended = args.regionFlags; handshake.RegionInfo4[0].RegionFlagsExtended = args.regionFlags;
handshake.RegionInfo4[0].RegionProtocols = 0; // 1 here would indicate that SSB is supported handshake.RegionInfo4[0].RegionProtocols = 0; // 1 here would indicate that SSB is supported
OutPacket(handshake, ThrottleOutPacketType.Task); OutPacket(handshake, ThrottleOutPacketType.Unknown);
} }
@ -3776,6 +3778,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ResendPrimUpdate(update); ResendPrimUpdate(update);
} }
// OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
// OpenSim.Framework.Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>();
// OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
// OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
//
// OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
// OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
// OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
// OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
private void ProcessEntityUpdates(int maxUpdates) private void ProcessEntityUpdates(int maxUpdates)
{ {
OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@ -3788,6 +3801,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
// objectUpdateBlocks.Value.Clear();
// compressedUpdateBlocks.Value.Clear();
// terseUpdateBlocks.Value.Clear();
// terseAgentUpdateBlocks.Value.Clear();
// objectUpdates.Value.Clear();
// compressedUpdates.Value.Clear();
// terseUpdates.Value.Clear();
// terseAgentUpdates.Value.Clear();
// Check to see if this is a flush // Check to see if this is a flush
if (maxUpdates <= 0) if (maxUpdates <= 0)
{ {
@ -4113,8 +4135,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
{ {
// if (!m_udpServer.IsRunningOutbound)
// return;
if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{ {
// if (!m_udpServer.IsRunningOutbound)
// return;
if (m_maxUpdates == 0 || m_LastQueueFill == 0) if (m_maxUpdates == 0 || m_LastQueueFill == 0)
{ {
m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
@ -4140,6 +4168,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit);
} }
internal bool HandleHasUpdates(ThrottleOutPacketTypeFlags categories)
{
bool hasUpdates = false;
if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{
if (m_entityUpdates.Count > 0)
hasUpdates = true;
else if (m_entityProps.Count > 0)
hasUpdates = true;
}
if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
{
if (ImageManager.HasUpdates())
hasUpdates = true;
}
return hasUpdates;
}
public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID)
{ {
AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); AssetUploadCompletePacket newPack = new AssetUploadCompletePacket();
@ -4961,7 +5010,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
ScenePresence presence = (ScenePresence)entity; ScenePresence presence = (ScenePresence)entity;
attachPoint = 0; attachPoint = presence.State;
collisionPlane = presence.CollisionPlane; collisionPlane = presence.CollisionPlane;
position = presence.OffsetPosition; position = presence.OffsetPosition;
velocity = presence.Velocity; velocity = presence.Velocity;
@ -4985,7 +5034,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
SceneObjectPart part = (SceneObjectPart)entity; SceneObjectPart part = (SceneObjectPart)entity;
attachPoint = part.ParentGroup.AttachmentPoint; attachPoint = part.ParentGroup.AttachmentPoint;
attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16));
// m_log.DebugFormat( // m_log.DebugFormat(
// "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}",
// attachPoint, part.Name, part.LocalId, Name); // attachPoint, part.Name, part.LocalId, Name);
@ -5013,7 +5062,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
pos += 4; pos += 4;
// Avatar/CollisionPlane // Avatar/CollisionPlane
data[pos++] = (byte)((attachPoint % 16) * 16 + (attachPoint / 16)); ; data[pos++] = (byte) attachPoint;
if (avatar) if (avatar)
{ {
data[pos++] = 1; data[pos++] = 1;
@ -5517,82 +5566,137 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Packet Handlers #region Packet Handlers
public int TotalAgentUpdates { get; set; }
#region Scene/Avatar #region Scene/Avatar
// Threshold for body rotation to be a significant agent update
private const float QDELTA = 0.000001f;
// Threshold for camera rotation to be a significant agent update
private const float VDELTA = 0.01f;
/// <summary>
/// This checks the update significance against the last update made.
/// </summary>
/// <remarks>Can only be called by one thread at a time</remarks>
/// <returns></returns>
/// <param name='x'></param>
public bool CheckAgentUpdateSignificance(AgentUpdatePacket.AgentDataBlock x)
{
return CheckAgentMovementUpdateSignificance(x) || CheckAgentCameraUpdateSignificance(x);
}
/// <summary>
/// This checks the movement/state update significance against the last update made.
/// </summary>
/// <remarks>Can only be called by one thread at a time</remarks>
/// <returns></returns>
/// <param name='x'></param>
private bool CheckAgentMovementUpdateSignificance(AgentUpdatePacket.AgentDataBlock x)
{
float qdelta1 = 1 - (float)Math.Pow(Quaternion.Dot(x.BodyRotation, m_thisAgentUpdateArgs.BodyRotation), 2);
//qdelta2 = 1 - (float)Math.Pow(Quaternion.Dot(x.HeadRotation, m_thisAgentUpdateArgs.HeadRotation), 2);
bool movementSignificant =
(qdelta1 > QDELTA) // significant if body rotation above threshold
// Ignoring head rotation altogether, because it's not being used for anything interesting up the stack
// || (qdelta2 > QDELTA * 10) // significant if head rotation above threshold
|| (x.ControlFlags != m_thisAgentUpdateArgs.ControlFlags) // significant if control flags changed
|| (x.ControlFlags != (byte)AgentManager.ControlFlags.NONE) // significant if user supplying any movement update commands
|| (x.Far != m_thisAgentUpdateArgs.Far) // significant if far distance changed
|| (x.Flags != m_thisAgentUpdateArgs.Flags) // significant if Flags changed
|| (x.State != m_thisAgentUpdateArgs.State) // significant if Stats changed
;
//if (movementSignificant)
//{
//m_log.DebugFormat("[LLCLIENTVIEW]: Bod {0} {1}",
// qdelta1, qdelta2);
//m_log.DebugFormat("[LLCLIENTVIEW]: St {0} {1} {2} {3}",
// x.ControlFlags, x.Flags, x.Far, x.State);
//}
return movementSignificant;
}
/// <summary>
/// This checks the camera update significance against the last update made.
/// </summary>
/// <remarks>Can only be called by one thread at a time</remarks>
/// <returns></returns>
/// <param name='x'></param>
private bool CheckAgentCameraUpdateSignificance(AgentUpdatePacket.AgentDataBlock x)
{
float vdelta1 = Vector3.Distance(x.CameraAtAxis, m_thisAgentUpdateArgs.CameraAtAxis);
float vdelta2 = Vector3.Distance(x.CameraCenter, m_thisAgentUpdateArgs.CameraCenter);
float vdelta3 = Vector3.Distance(x.CameraLeftAxis, m_thisAgentUpdateArgs.CameraLeftAxis);
float vdelta4 = Vector3.Distance(x.CameraUpAxis, m_thisAgentUpdateArgs.CameraUpAxis);
bool cameraSignificant =
(vdelta1 > VDELTA) ||
(vdelta2 > VDELTA) ||
(vdelta3 > VDELTA) ||
(vdelta4 > VDELTA)
;
//if (cameraSignificant)
//{
//m_log.DebugFormat("[LLCLIENTVIEW]: Cam1 {0} {1}",
// x.CameraAtAxis, x.CameraCenter);
//m_log.DebugFormat("[LLCLIENTVIEW]: Cam2 {0} {1}",
// x.CameraLeftAxis, x.CameraUpAxis);
//}
return cameraSignificant;
}
private bool HandleAgentUpdate(IClientAPI sener, Packet packet) private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
{ {
if (OnAgentUpdate != null) // We got here, which means that something in agent update was significant
{
AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
#region Packet Session and User Check
if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId)
{
PacketPool.Instance.ReturnPacket(packet);
return false;
}
#endregion
bool update = false;
AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
if (m_lastAgentUpdateArgs != null) if (x.AgentID != AgentId || x.SessionID != SessionId)
{ return false;
// These should be ordered from most-likely to
// least likely to change. I've made an initial
// guess at that.
update =
(
(x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
(x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
(x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
(x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
(x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
(x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
(x.Far != m_lastAgentUpdateArgs.Far) ||
(x.Flags != m_lastAgentUpdateArgs.Flags) ||
(x.State != m_lastAgentUpdateArgs.State) ||
(x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
(x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
(x.AgentID != m_lastAgentUpdateArgs.AgentID)
);
}
else
{
m_lastAgentUpdateArgs = new AgentUpdateArgs();
update = true;
}
if (update) // Before we update the current m_thisAgentUpdateArgs, let's check this again
{ // to see what exactly changed
// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); bool movement = CheckAgentMovementUpdateSignificance(x);
bool camera = CheckAgentCameraUpdateSignificance(x);
m_lastAgentUpdateArgs.AgentID = x.AgentID; m_thisAgentUpdateArgs.AgentID = x.AgentID;
m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation; m_thisAgentUpdateArgs.BodyRotation = x.BodyRotation;
m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; m_thisAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter; m_thisAgentUpdateArgs.CameraCenter = x.CameraCenter;
m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; m_thisAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; m_thisAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags; m_thisAgentUpdateArgs.ControlFlags = x.ControlFlags;
m_lastAgentUpdateArgs.Far = x.Far; m_thisAgentUpdateArgs.Far = x.Far;
m_lastAgentUpdateArgs.Flags = x.Flags; m_thisAgentUpdateArgs.Flags = x.Flags;
m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation; m_thisAgentUpdateArgs.HeadRotation = x.HeadRotation;
m_lastAgentUpdateArgs.SessionID = x.SessionID; m_thisAgentUpdateArgs.SessionID = x.SessionID;
m_lastAgentUpdateArgs.State = x.State; m_thisAgentUpdateArgs.State = x.State;
UpdateAgent handlerAgentUpdate = OnAgentUpdate; UpdateAgent handlerAgentUpdate = OnAgentUpdate;
UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
UpdateAgent handlerAgentCameraUpdate = OnAgentCameraUpdate;
// Was there a significant movement/state change?
if (movement)
{
if (handlerPreAgentUpdate != null) if (handlerPreAgentUpdate != null)
OnPreAgentUpdate(this, m_lastAgentUpdateArgs); OnPreAgentUpdate(this, m_thisAgentUpdateArgs);
if (handlerAgentUpdate != null) if (handlerAgentUpdate != null)
OnAgentUpdate(this, m_lastAgentUpdateArgs); OnAgentUpdate(this, m_thisAgentUpdateArgs);
}
// Was there a significant camera(s) change?
if (camera)
if (handlerAgentCameraUpdate != null)
handlerAgentCameraUpdate(this, m_thisAgentUpdateArgs);
handlerAgentUpdate = null; handlerAgentUpdate = null;
handlerPreAgentUpdate = null; handlerPreAgentUpdate = null;
} handlerAgentCameraUpdate = null;
}
PacketPool.Instance.ReturnPacket(packet); PacketPool.Instance.ReturnPacket(packet);
@ -12441,7 +12545,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(dialog, ThrottleOutPacketType.Task); OutPacket(dialog, ThrottleOutPacketType.Task);
} }
public void StopFlying(ISceneEntity p) public void SendAgentTerseUpdate(ISceneEntity p)
{ {
if (p is ScenePresence) if (p is ScenePresence)
{ {
@ -12455,25 +12559,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Vector3 pos = presence.AbsolutePosition; Vector3 pos = presence.AbsolutePosition;
if (presence.Appearance.AvatarHeight != 127.0f)
pos += new Vector3(0f, 0f, (presence.Appearance.AvatarHeight/6f));
else
pos += new Vector3(0f, 0f, (1.56f/6f));
presence.AbsolutePosition = pos;
// attach a suitable collision plane regardless of the actual situation to force the LLClient to land.
// Collision plane below the avatar's position a 6th of the avatar's height is suitable.
// Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a
// certain amount.. because the LLClient wouldn't land in that situation anyway.
// why are we still testing for this really old height value default???
if (presence.Appearance.AvatarHeight != 127.0f)
presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - presence.Appearance.AvatarHeight/6f);
else
presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f/6f));
ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = ImprovedTerseObjectUpdatePacket.ObjectDataBlock block =
CreateImprovedTerseBlock(p, false); CreateImprovedTerseBlock(p, false);

View File

@ -206,6 +206,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public bool HasUpdates()
{
J2KImage image = GetHighestPriorityImage();
return image != null && image.IsDecoded;
}
public bool ProcessImageQueue(int packetsToSend) public bool ProcessImageQueue(int packetsToSend)
{ {
int packetsSent = 0; int packetsSent = 0;

View File

@ -31,6 +31,7 @@ using System.Net;
using System.Threading; using System.Threading;
using log4net; using log4net;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Packets; using OpenMetaverse.Packets;
@ -81,6 +82,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// hooked to put more data on the empty queue</summary> /// hooked to put more data on the empty queue</summary>
public event QueueEmpty OnQueueEmpty; public event QueueEmpty OnQueueEmpty;
public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates;
/// <summary>AgentID for this client</summary> /// <summary>AgentID for this client</summary>
public readonly UUID AgentID; public readonly UUID AgentID;
/// <summary>The remote address of the connected client</summary> /// <summary>The remote address of the connected client</summary>
@ -613,14 +616,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="categories">Throttle categories to fire the callback for</param> /// <param name="categories">Throttle categories to fire the callback for</param>
private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
{ {
if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
{ {
m_isQueueEmptyRunning = true;
int start = Environment.TickCount & Int32.MaxValue;
const int MIN_CALLBACK_MS = 30;
m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
if (m_nextOnQueueEmpty == 0)
m_nextOnQueueEmpty = 1;
// Use a value of 0 to signal that FireQueueEmpty is running // Use a value of 0 to signal that FireQueueEmpty is running
m_nextOnQueueEmpty = 0; // m_nextOnQueueEmpty = 0;
m_categories = categories;
if (HasUpdates(m_categories))
{
// Asynchronously run the callback // Asynchronously run the callback
Util.FireAndForget(FireQueueEmpty, categories); Util.FireAndForget(FireQueueEmpty, categories);
} }
else
{
m_isQueueEmptyRunning = false;
} }
}
}
private bool m_isQueueEmptyRunning;
private ThrottleOutPacketTypeFlags m_categories = 0;
/// <summary> /// <summary>
/// Fires the OnQueueEmpty callback and sets the minimum time that it /// Fires the OnQueueEmpty callback and sets the minimum time that it
@ -631,22 +657,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// signature</param> /// signature</param>
private void FireQueueEmpty(object o) private void FireQueueEmpty(object o)
{ {
const int MIN_CALLBACK_MS = 30; // int start = Environment.TickCount & Int32.MaxValue;
// const int MIN_CALLBACK_MS = 30;
// if (m_udpServer.IsRunningOutbound)
// {
ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
QueueEmpty callback = OnQueueEmpty; QueueEmpty callback = OnQueueEmpty;
int start = Environment.TickCount & Int32.MaxValue;
if (callback != null) if (callback != null)
{ {
// if (m_udpServer.IsRunningOutbound)
// {
try { callback(categories); } try { callback(categories); }
catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
// }
} }
// }
m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; // m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
if (m_nextOnQueueEmpty == 0) // if (m_nextOnQueueEmpty == 0)
m_nextOnQueueEmpty = 1; // m_nextOnQueueEmpty = 1;
// }
m_isQueueEmptyRunning = false;
} }
/// <summary> /// <summary>

View File

@ -67,11 +67,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
m_udpServer.AddScene(scene); m_udpServer.AddScene(scene);
StatsManager.RegisterStat(
new Stat(
"IncomingUDPReceivesCount",
"Number of UDP receives performed",
"Number of UDP receives performed",
"",
"clientstack",
scene.Name,
StatType.Pull,
MeasuresOfInterest.AverageChangeOverTime,
stat => stat.Value = m_udpServer.UdpReceives,
StatVerbosity.Debug));
StatsManager.RegisterStat( StatsManager.RegisterStat(
new Stat( new Stat(
"IncomingPacketsProcessedCount", "IncomingPacketsProcessedCount",
"Number of inbound UDP packets processed", "Number of inbound LL protocol packets processed",
"Number of inbound UDP packets processed", "Number of inbound LL protocol packets processed",
"", "",
"clientstack", "clientstack",
scene.Name, scene.Name,
@ -79,6 +92,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
MeasuresOfInterest.AverageChangeOverTime, MeasuresOfInterest.AverageChangeOverTime,
stat => stat.Value = m_udpServer.IncomingPacketsProcessed, stat => stat.Value = m_udpServer.IncomingPacketsProcessed,
StatVerbosity.Debug)); StatVerbosity.Debug));
StatsManager.RegisterStat(
new Stat(
"OutgoingUDPSendsCount",
"Number of UDP sends performed",
"Number of UDP sends performed",
"",
"clientstack",
scene.Name,
StatType.Pull,
MeasuresOfInterest.AverageChangeOverTime,
stat => stat.Value = m_udpServer.UdpSends,
StatVerbosity.Debug));
StatsManager.RegisterStat(
new Stat(
"AverageUDPProcessTime",
"Average number of milliseconds taken to process each incoming UDP packet in a sample.",
"This is for initial receive processing which is separate from the later client LL packet processing stage.",
"ms",
"clientstack",
scene.Name,
StatType.Pull,
MeasuresOfInterest.None,
stat => stat.Value = m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond,
// stat =>
// stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, 7),
StatVerbosity.Debug));
} }
public bool HandlesRegion(Location x) public bool HandlesRegion(Location x)
@ -182,6 +223,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Flag to signal when clients should send pings</summary> /// <summary>Flag to signal when clients should send pings</summary>
protected bool m_sendPing; protected bool m_sendPing;
/// <summary>
/// Event used to signal when queued packets are available for sending.
/// </summary>
/// <remarks>
/// This allows the outbound loop to only operate when there is data to send rather than continuously polling.
/// Some data is sent immediately and not queued. That data would not trigger this event.
/// </remarks>
private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false);
private Pool<IncomingPacket> m_incomingPacketPool; private Pool<IncomingPacket> m_incomingPacketPool;
/// <summary> /// <summary>
@ -459,6 +509,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_scene = (Scene)scene; m_scene = (Scene)scene;
m_location = new Location(m_scene.RegionInfo.RegionHandle); m_location = new Location(m_scene.RegionInfo.RegionHandle);
StatsManager.RegisterStat(
new Stat(
"InboxPacketsCount",
"Number of LL protocol packets waiting for the second stage of processing after initial receive.",
"Number of LL protocol packets waiting for the second stage of processing after initial receive.",
"",
"clientstack",
scene.Name,
StatType.Pull,
MeasuresOfInterest.AverageChangeOverTime,
stat => stat.Value = packetInbox.Count,
StatVerbosity.Debug));
// XXX: These stats are also pool stats but we register them separately since they are currently not // XXX: These stats are also pool stats but we register them separately since they are currently not
// turned on and off by EnablePools()/DisablePools() // turned on and off by EnablePools()/DisablePools()
StatsManager.RegisterStat( StatsManager.RegisterStat(
@ -572,6 +635,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
"debug lludp status", "debug lludp status",
"Return status of LLUDP packet processing.", "Return status of LLUDP packet processing.",
HandleStatusCommand); 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) private void HandlePacketCommand(string module, string[] args)
@ -706,6 +777,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
bool m_discardAgentUpdates;
private void HandleAgentUpdateCommand(string module, string[] args)
{
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene)
return;
m_discardAgentUpdates = !m_discardAgentUpdates;
MainConsole.Instance.OutputFormat(
"Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, m_scene.Name);
}
private void HandleStatusCommand(string module, string[] args) private void HandleStatusCommand(string module, string[] args)
{ {
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene)
@ -806,6 +890,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
PacketPool.Instance.ReturnPacket(packet); PacketPool.Instance.ReturnPacket(packet);
m_dataPresentEvent.Set();
} }
/// <summary> /// <summary>
@ -1179,6 +1265,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return; return;
} }
else if (packet.Type == PacketType.CompleteAgentMovement)
{
// Send ack straight away to let the viewer know that we got it.
SendAckImmediate(endPoint, packet.Header.Sequence);
// We need to copy the endpoint so that it doesn't get changed when another thread reuses the
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
return;
}
// Determine which agent this packet came from // Determine which agent this packet came from
IClientAPI client; IClientAPI client;
@ -1282,6 +1381,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length);
#endregion BinaryStats #endregion BinaryStats
if (packet.Type == PacketType.AgentUpdate)
{
if (m_discardAgentUpdates)
return;
((LLClientView)client).TotalAgentUpdates++;
AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
LLClientView llClient = client as LLClientView;
if (agentUpdate.AgentData.SessionID != client.SessionId
|| agentUpdate.AgentData.AgentID != client.AgentId
|| !(llClient == null || llClient.CheckAgentUpdateSignificance(agentUpdate.AgentData)) )
{
PacketPool.Instance.ReturnPacket(packet);
return;
}
}
#region Ping Check Handling #region Ping Check Handling
if (packet.Type == PacketType.StartPingCheck) if (packet.Type == PacketType.StartPingCheck)
@ -1499,6 +1617,74 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
private void HandleCompleteMovementIntoRegion(object o)
{
IPEndPoint endPoint = null;
IClientAPI client = null;
try
{
object[] array = (object[])o;
endPoint = (IPEndPoint)array[0];
CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1];
// Determine which agent this packet came from
int count = 20;
bool ready = false;
while (!ready && count-- > 0)
{
// Let's make sure there is an active client attached to a scene presence,
// otherwise there won't be any handlers for this packet
if (m_scene.TryGetClient(endPoint, out client) && client.IsActive && client.SceneAgent != null)
{
LLClientView llClientView = (LLClientView)client;
LLUDPClient udpClient = llClientView.UDPClient;
if (udpClient != null && udpClient.IsConnected)
ready = true;
else
{
m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)");
Thread.Sleep(200);
}
}
else
{
m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)");
Thread.Sleep(200);
}
}
if (client == null)
return;
IncomingPacket incomingPacket1;
// Inbox insertion
if (UsePools)
{
incomingPacket1 = m_incomingPacketPool.GetObject();
incomingPacket1.Client = (LLClientView)client;
incomingPacket1.Packet = packet;
}
else
{
incomingPacket1 = new IncomingPacket((LLClientView)client, packet);
}
packetInbox.Enqueue(incomingPacket1);
}
catch (Exception e)
{
m_log.ErrorFormat(
"[LLUDPSERVER]: CompleteMovementIntoRegion handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown",
e.Message,
e.StackTrace);
}
}
/// <summary> /// <summary>
/// Send an ack immediately to the given endpoint. /// Send an ack immediately to the given endpoint.
/// </summary> /// </summary>
@ -1718,8 +1904,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// If nothing was sent, sleep for the minimum amount of time before a // If nothing was sent, sleep for the minimum amount of time before a
// token bucket could get more tokens // token bucket could get more tokens
if (!m_packetSent) //if (!m_packetSent)
Thread.Sleep((int)TickCountResolution); // Thread.Sleep((int)TickCountResolution);
//
// Instead, now wait for data present to be explicitly signalled. Evidence so far is that with
// modern mono it reduces CPU base load since there is no more continuous polling.
m_dataPresentEvent.WaitOne(100);
Watchdog.UpdateThread(); Watchdog.UpdateThread();
} }

View File

@ -77,6 +77,36 @@ namespace OpenMetaverse
/// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks> /// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks>
public bool IsRunningOutbound { get; private set; } public bool IsRunningOutbound { get; private set; }
/// <summary>
/// Number of UDP receives.
/// </summary>
public int UdpReceives { get; private set; }
/// <summary>
/// Number of UDP sends
/// </summary>
public int UdpSends { get; private set; }
/// <summary>
/// Number of receives over which to establish a receive time average.
/// </summary>
private readonly static int s_receiveTimeSamples = 500;
/// <summary>
/// Current number of samples taken to establish a receive time average.
/// </summary>
private int m_currentReceiveTimeSamples;
/// <summary>
/// Cumulative receive time for the sample so far.
/// </summary>
private int m_receiveTicksInCurrentSamplePeriod;
/// <summary>
/// The average time taken for each require receive in the last sample.
/// </summary>
public float AverageReceiveTicksForLastSamplePeriod { get; private set; }
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
@ -111,6 +141,8 @@ namespace OpenMetaverse
if (!IsRunningInbound) if (!IsRunningInbound)
{ {
m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop");
const int SIO_UDP_CONNRESET = -1744830452; const int SIO_UDP_CONNRESET = -1744830452;
IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
@ -155,6 +187,8 @@ namespace OpenMetaverse
/// </summary> /// </summary>
public void StartOutbound() public void StartOutbound()
{ {
m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
IsRunningOutbound = true; IsRunningOutbound = true;
} }
@ -162,10 +196,8 @@ namespace OpenMetaverse
{ {
if (IsRunningInbound) if (IsRunningInbound)
{ {
// wait indefinitely for a writer lock. Once this is called, the .NET runtime m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop");
// will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set IsRunningInbound = false to inform the other
// threads that the socket is closed.
IsRunningInbound = false; IsRunningInbound = false;
m_udpSocket.Close(); m_udpSocket.Close();
} }
@ -173,6 +205,8 @@ namespace OpenMetaverse
public void StopOutbound() public void StopOutbound()
{ {
m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
IsRunningOutbound = false; IsRunningOutbound = false;
} }
@ -271,6 +305,8 @@ namespace OpenMetaverse
// to AsyncBeginReceive // to AsyncBeginReceive
if (IsRunningInbound) if (IsRunningInbound)
{ {
UdpReceives++;
// Asynchronous mode will start another receive before the // Asynchronous mode will start another receive before the
// callback for this packet is even fired. Very parallel :-) // callback for this packet is even fired. Very parallel :-)
if (m_asyncPacketHandling) if (m_asyncPacketHandling)
@ -282,6 +318,8 @@ namespace OpenMetaverse
try try
{ {
int startTick = Util.EnvironmentTickCount();
// get the length of data actually read from the socket, store it with the // get the length of data actually read from the socket, store it with the
// buffer // buffer
buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
@ -289,6 +327,23 @@ namespace OpenMetaverse
// call the abstract method PacketReceived(), passing the buffer that // call the abstract method PacketReceived(), passing the buffer that
// has just been filled from the socket read. // has just been filled from the socket read.
PacketReceived(buffer); PacketReceived(buffer);
// If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler)
// then a particular stat may be inaccurate due to a race condition. We won't worry about this
// since this should be rare and won't cause a runtime problem.
if (m_currentReceiveTimeSamples >= s_receiveTimeSamples)
{
AverageReceiveTicksForLastSamplePeriod
= (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples;
m_receiveTicksInCurrentSamplePeriod = 0;
m_currentReceiveTimeSamples = 0;
}
else
{
m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick);
m_currentReceiveTimeSamples++;
}
} }
catch (SocketException) { } catch (SocketException) { }
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
@ -302,14 +357,13 @@ namespace OpenMetaverse
if (!m_asyncPacketHandling) if (!m_asyncPacketHandling)
AsyncBeginReceive(); AsyncBeginReceive();
} }
} }
} }
public void AsyncBeginSend(UDPPacketBuffer buf) public void AsyncBeginSend(UDPPacketBuffer buf)
{ {
if (IsRunningOutbound) // if (IsRunningOutbound)
{ // {
try try
{ {
m_udpSocket.BeginSendTo( m_udpSocket.BeginSendTo(
@ -323,7 +377,7 @@ namespace OpenMetaverse
} }
catch (SocketException) { } catch (SocketException) { }
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
} // }
} }
void AsyncEndSend(IAsyncResult result) void AsyncEndSend(IAsyncResult result)
@ -332,6 +386,8 @@ namespace OpenMetaverse
{ {
// UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
m_udpSocket.EndSendTo(result); m_udpSocket.EndSendTo(result);
UdpSends++;
} }
catch (SocketException) { } catch (SocketException) { }
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }

View File

@ -684,6 +684,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath();
} }
if (version.Equals("SIMULATION/0.2"))
TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
else
TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
}
private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
{
ulong destinationHandle = finalDestination.RegionHandle;
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Using TP V1");
// Let's create an agent there if one doesn't exist yet. // Let's create an agent there if one doesn't exist yet.
// NOTE: logout will always be false for a non-HG teleport. // NOTE: logout will always be false for a non-HG teleport.
bool logout = false; bool logout = false;
@ -728,6 +742,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
sp.CloseChildAgents(newRegionX, newRegionY); sp.CloseChildAgents(newRegionX, newRegionY);
IClientIPEndpoint ipepClient; IClientIPEndpoint ipepClient;
string capsPath = String.Empty;
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
{ {
m_log.DebugFormat( m_log.DebugFormat(
@ -778,10 +793,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// Let's send a full update of the agent. This is a synchronous call. // Let's send a full update of the agent. This is a synchronous call.
AgentData agent = new AgentData(); AgentData agent = new AgentData();
sp.CopyTo(agent); sp.CopyTo(agent);
agent.Position = position; agent.Position = agentCircuit.startpos;
SetCallbackURL(agent, sp.Scene.RegionInfo); SetCallbackURL(agent, sp.Scene.RegionInfo);
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
// We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to
// establish th econnection to the destination which makes it return true. // establish th econnection to the destination which makes it return true.
@ -843,6 +857,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// closes our existing agent which is still signalled as root. // closes our existing agent which is still signalled as root.
sp.IsChildAgent = true; sp.IsChildAgent = true;
// OK, send TPFinish to the client, so that it starts the process of contacting the destination region
if (m_eqModule != null) if (m_eqModule != null)
{ {
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID); m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
@ -901,13 +916,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
{ {
// We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before // RED ALERT!!!!
// they regard the new region as the current region after receiving the AgentMovementComplete // PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES.
// response. If close is sent before then, it will cause the viewer to quit instead. // THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion
// // BEFORE THEY SETTLE IN THE NEW REGION.
// This sleep can be increased if necessary. However, whilst it's active, // DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR
// an agent cannot teleport back to this region if it has teleported away. // IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS.
Thread.Sleep(2000); Thread.Sleep(5000);
sp.Scene.IncomingCloseAgent(sp.UUID, false); sp.Scene.IncomingCloseAgent(sp.UUID, false);
} }
@ -918,6 +933,134 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
} }
} }
private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
{
ulong destinationHandle = finalDestination.RegionHandle;
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
// Let's create an agent there if one doesn't exist yet.
// NOTE: logout will always be false for a non-HG teleport.
bool logout = false;
if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
{
m_interRegionTeleportFailures.Value++;
sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason));
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}",
sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason);
return;
}
// Past this point we have to attempt clean up if the teleport fails, so update transfer state.
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
IClientIPEndpoint ipepClient;
string capsPath = String.Empty;
if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
{
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}",
finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
#region IP Translation for NAT
// Uses ipepClient above
if (sp.ClientView.TryGet(out ipepClient))
{
endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
}
#endregion
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
else
{
agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle);
capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
}
// We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator,
// where that neighbour simulator could otherwise request a child agent create on the source which then
// closes our existing agent which is still signalled as root.
//sp.IsChildAgent = true;
// New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid
if (m_eqModule != null)
m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID);
else
sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
teleportFlags, capsPath);
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
// Let's send a full update of the agent.
AgentData agent = new AgentData();
sp.CopyTo(agent);
agent.Position = agentCircuit.startpos;
agent.SenderWantsToWaitForRoot = true;
//SetCallbackURL(agent, sp.Scene.RegionInfo);
// Send the Update. If this returns true, we know the client has contacted the destination
// via CompleteMovementIntoRegion, so we can let go.
// If it returns false, something went wrong, and we need to abort.
if (!UpdateAgent(reg, finalDestination, agent, sp))
{
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
return;
}
m_log.WarnFormat(
"[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.",
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
return;
}
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
// May need to logout or other cleanup
AgentHasMovedAway(sp, logout);
// Well, this is it. The agent is over there.
KillEntity(sp.Scene, sp.LocalId);
// Now let's make it officially a child agent
sp.MakeChildAgent();
// OK, it got this agent. Let's close some child agents
sp.CloseChildAgents(newRegionX, newRegionY);
// Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone
if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
{
// RED ALERT!!!!
// PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES.
// THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion
// BEFORE THEY SETTLE IN THE NEW REGION.
// DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR
// IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS.
Thread.Sleep(5000);
sp.Scene.IncomingCloseAgent(sp.UUID, false);
}
else
// now we have a child agent in this region.
sp.Reset();
}
/// <summary> /// <summary>
/// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
/// </summary> /// </summary>
@ -931,11 +1074,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{ {
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
if (sp.IsChildAgent) // We had set it to child before attempted TP (V1)
{
sp.IsChildAgent = false; sp.IsChildAgent = false;
ReInstantiateScripts(sp); ReInstantiateScripts(sp);
EnableChildAgents(sp); EnableChildAgents(sp);
}
// Finally, kill the agent we just created at the destination. // Finally, kill the agent we just created at the destination.
// XXX: Possibly this should be done asynchronously. // XXX: Possibly this should be done asynchronously.
Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token); Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token);

View File

@ -185,8 +185,11 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
} }
} }
public void UploadInventoryItem(UUID avatarID, UUID assetID, string name, int userlevel) public void UploadInventoryItem(UUID avatarID, AssetType type, UUID assetID, string name, int userlevel)
{ {
if (type == AssetType.Link)
return;
string userAssetServer = string.Empty; string userAssetServer = string.Empty;
if (IsForeignUser(avatarID, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission) if (IsForeignUser(avatarID, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission)
{ {
@ -221,7 +224,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
{ {
UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data); UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data);
UploadInventoryItem(remoteClient.AgentId, newAssetID, "", 0); UploadInventoryItem(remoteClient.AgentId, AssetType.Unknown, newAssetID, "", 0);
return newAssetID; return newAssetID;
} }
@ -232,7 +235,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
protected override void ExportAsset(UUID agentID, UUID assetID) protected override void ExportAsset(UUID agentID, UUID assetID)
{ {
if (!assetID.Equals(UUID.Zero)) if (!assetID.Equals(UUID.Zero))
UploadInventoryItem(agentID, assetID, "", 0); UploadInventoryItem(agentID, AssetType.Unknown, assetID, "", 0);
else else
m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); m_log.Debug("[HGScene]: Scene.Inventory did not create asset");
} }
@ -348,7 +351,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId); InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId);
InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID); InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID);
inv.SendBulkUpdateInventory(content.Folders.ToArray(), content.Items.ToArray()); List<InventoryFolderBase> keep = new List<InventoryFolderBase>();
foreach (InventoryFolderBase f in content.Folders)
{
if (f.Name != "My Suitcase" && f.Name != "Current Outfit")
keep.Add(f);
}
inv.SendBulkUpdateInventory(keep.ToArray(), content.Items.ToArray());
} }
} }
} }
@ -381,7 +392,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
foreach (InventoryFolderBase f in content.Folders) foreach (InventoryFolderBase f in content.Folders)
{ {
if (f.Name != "My Suitcase") if (f.Name != "My Suitcase" && f.Name != "Current Outfit")
{ {
f.Name = f.Name + " (Unavailable)"; f.Name = f.Name + " (Unavailable)";
keep.Add(f); keep.Add(f);

View File

@ -244,8 +244,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory
if (inventoryURL != null && inventoryURL != string.Empty) if (inventoryURL != null && inventoryURL != string.Empty)
{ {
inventoryURL = inventoryURL.Trim(new char[] { '/' }); inventoryURL = inventoryURL.Trim(new char[] { '/' });
lock (m_InventoryURLs)
if (!m_InventoryURLs.ContainsKey(userID))
{
m_InventoryURLs.Add(userID, inventoryURL); m_InventoryURLs.Add(userID, inventoryURL);
m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Added {0} to the cache of inventory URLs", inventoryURL); m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Added {0} to the cache of inventory URLs", inventoryURL);
}
return; return;
} }
} }

View File

@ -79,7 +79,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence
public void OnConnectionClose(IClientAPI client) public void OnConnectionClose(IClientAPI client)
{ {
if (!client.SceneAgent.IsChildAgent) if (client != null && client.SceneAgent != null && !client.SceneAgent.IsChildAgent)
{ {
// m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); // m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName);
m_PresenceService.LogoutAgent(client.SessionId); m_PresenceService.LogoutAgent(client.SessionId);

View File

@ -48,7 +48,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
/// <summary> /// <summary>
/// Version of this service /// Version of this service
/// </summary> /// </summary>
private const string m_Version = "SIMULATION/0.1"; private const string m_Version = "SIMULATION/0.2";
/// <summary> /// <summary>
/// Map region ID to scene. /// Map region ID to scene.

View File

@ -742,7 +742,7 @@ namespace OpenSim.Region.Framework.Scenes
public event OnIncomingSceneObjectDelegate OnIncomingSceneObject; public event OnIncomingSceneObjectDelegate OnIncomingSceneObject;
public delegate void OnIncomingSceneObjectDelegate(SceneObjectGroup so); public delegate void OnIncomingSceneObjectDelegate(SceneObjectGroup so);
public delegate void NewInventoryItemUploadComplete(UUID avatarID, UUID assetID, string name, int userlevel); public delegate void NewInventoryItemUploadComplete(UUID avatarID, AssetType type, UUID assetID, string name, int userlevel);
public event NewInventoryItemUploadComplete OnNewInventoryItemUploadComplete; public event NewInventoryItemUploadComplete OnNewInventoryItemUploadComplete;
@ -2146,7 +2146,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
public void TriggerOnNewInventoryItemUploadComplete(UUID agentID, UUID AssetID, String AssetName, int userlevel) public void TriggerOnNewInventoryItemUploadComplete(UUID agentID, AssetType type, UUID AssetID, String AssetName, int userlevel)
{ {
NewInventoryItemUploadComplete handlerNewInventoryItemUpdateComplete = OnNewInventoryItemUploadComplete; NewInventoryItemUploadComplete handlerNewInventoryItemUpdateComplete = OnNewInventoryItemUploadComplete;
if (handlerNewInventoryItemUpdateComplete != null) if (handlerNewInventoryItemUpdateComplete != null)
@ -2155,7 +2155,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
try try
{ {
d(agentID, AssetID, AssetName, userlevel); d(agentID, type, AssetID, AssetName, userlevel);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -139,7 +139,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
userlevel = 1; userlevel = 1;
} }
EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel);
return true; return true;
} }
@ -178,7 +178,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
userlevel = 1; userlevel = 1;
} }
EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel);
if (originalFolder != UUID.Zero) if (originalFolder != UUID.Zero)
{ {

View File

@ -390,6 +390,7 @@ namespace OpenSim.Region.Framework.Scenes
void ProcessViewerEffect(IClientAPI remoteClient, List<ViewerEffectEventHandlerArg> args) void ProcessViewerEffect(IClientAPI remoteClient, List<ViewerEffectEventHandlerArg> args)
{ {
// TODO: don't create new blocks if recycling an old packet // TODO: don't create new blocks if recycling an old packet
bool discardableEffects = true;
ViewerEffectPacket.EffectBlock[] effectBlockArray = new ViewerEffectPacket.EffectBlock[args.Count]; ViewerEffectPacket.EffectBlock[] effectBlockArray = new ViewerEffectPacket.EffectBlock[args.Count];
for (int i = 0; i < args.Count; i++) for (int i = 0; i < args.Count; i++)
{ {
@ -401,15 +402,32 @@ namespace OpenSim.Region.Framework.Scenes
effect.Type = args[i].Type; effect.Type = args[i].Type;
effect.TypeData = args[i].TypeData; effect.TypeData = args[i].TypeData;
effectBlockArray[i] = effect; effectBlockArray[i] = effect;
if ((EffectType)effect.Type != EffectType.LookAt && (EffectType)effect.Type != EffectType.Beam)
discardableEffects = false;
//m_log.DebugFormat("[YYY]: VE {0} {1} {2}", effect.AgentID, effect.Duration, (EffectType)effect.Type);
} }
ForEachClient( ForEachScenePresence(sp =>
delegate(IClientAPI client)
{ {
if (client.AgentId != remoteClient.AgentId) if (sp.ControllingClient.AgentId != remoteClient.AgentId)
client.SendViewerEffect(effectBlockArray); {
if (!discardableEffects ||
(discardableEffects && ShouldSendDiscardableEffect(remoteClient, sp)))
{
//m_log.DebugFormat("[YYY]: Sending to {0}", sp.UUID);
sp.ControllingClient.SendViewerEffect(effectBlockArray);
} }
); //else
// m_log.DebugFormat("[YYY]: Not sending to {0}", sp.UUID);
}
});
}
private bool ShouldSendDiscardableEffect(IClientAPI thisClient, ScenePresence other)
{
return Vector3.Distance(other.CameraPosition, thisClient.SceneAgent.AbsolutePosition) < 10;
} }
/// <summary> /// <summary>

View File

@ -230,6 +230,8 @@ namespace OpenSim.Region.Framework.Scenes
public int MaxUndoCount { get; set; } public int MaxUndoCount { get; set; }
public bool SeeIntoRegion { get; set; }
// Using this for RegionReady module to prevent LoginsDisabled from changing under our feet; // Using this for RegionReady module to prevent LoginsDisabled from changing under our feet;
public bool LoginLock = false; public bool LoginLock = false;
@ -839,6 +841,8 @@ namespace OpenSim.Region.Framework.Scenes
//Animation states //Animation states
m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false); m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false);
SeeIntoRegion = startupConfig.GetBoolean("see_into_region", true);
MaxUndoCount = startupConfig.GetInt("MaxPrimUndos", 20); MaxUndoCount = startupConfig.GetInt("MaxPrimUndos", 20);
PhysicalPrims = startupConfig.GetBoolean("physical_prim", PhysicalPrims); PhysicalPrims = startupConfig.GetBoolean("physical_prim", PhysicalPrims);
@ -3735,7 +3739,7 @@ namespace OpenSim.Region.Framework.Scenes
try try
{ {
if (!AuthorizeUser(agent, out reason)) if (!AuthorizeUser(agent, SeeIntoRegion, out reason))
{ {
m_authenticateHandler.RemoveCircuit(agent.circuitcode); m_authenticateHandler.RemoveCircuit(agent.circuitcode);
return false; return false;
@ -3975,7 +3979,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="reason">outputs the reason to this string</param> /// <param name="reason">outputs the reason to this string</param>
/// <returns>True if the region accepts this agent. False if it does not. False will /// <returns>True if the region accepts this agent. False if it does not. False will
/// also return a reason.</returns> /// also return a reason.</returns>
protected virtual bool AuthorizeUser(AgentCircuitData agent, out string reason) protected virtual bool AuthorizeUser(AgentCircuitData agent, bool bypassAccessControl, out string reason)
{ {
reason = String.Empty; reason = String.Empty;
@ -4010,6 +4014,12 @@ namespace OpenSim.Region.Framework.Scenes
m_log.ErrorFormat("[CONNECTION BEGIN]: Estate Settings is null!"); m_log.ErrorFormat("[CONNECTION BEGIN]: Estate Settings is null!");
} }
// We only test the things below when we want to cut off
// child agents from being present in the scene for which their root
// agent isn't allowed. Otherwise, we allow child agents. The test for
// the root is done elsewhere (QueryAccess)
if (!bypassAccessControl)
{
List<UUID> agentGroups = new List<UUID>(); List<UUID> agentGroups = new List<UUID>();
if (m_groupsModule != null) if (m_groupsModule != null)
@ -4056,6 +4066,7 @@ namespace OpenSim.Region.Framework.Scenes
RegionInfo.RegionName); RegionInfo.RegionName);
return false; return false;
} }
}
// TODO: estate/region settings are not properly hooked up // TODO: estate/region settings are not properly hooked up
// to ILandObject.isRestrictedFromLand() // to ILandObject.isRestrictedFromLand()
@ -4203,13 +4214,23 @@ namespace OpenSim.Region.Framework.Scenes
if (childAgentUpdate != null) if (childAgentUpdate != null)
{ {
if (cAgentData.SessionID != childAgentUpdate.ControllingClient.SessionId) if (cAgentData.SessionID != childAgentUpdate.ControllingClient.SessionId)
{
m_log.WarnFormat("[SCENE]: Attempt to update agent {0} with invalid session id {1} (possibly from simulator in older version; tell them to update).", childAgentUpdate.UUID, cAgentData.SessionID); m_log.WarnFormat("[SCENE]: Attempt to update agent {0} with invalid session id {1} (possibly from simulator in older version; tell them to update).", childAgentUpdate.UUID, cAgentData.SessionID);
Console.WriteLine(String.Format("[SCENE]: Attempt to update agent {0} ({1}) with invalid session id {2}",
childAgentUpdate.UUID, childAgentUpdate.ControllingClient.SessionId, cAgentData.SessionID));
}
childAgentUpdate.ChildAgentDataUpdate(cAgentData); childAgentUpdate.ChildAgentDataUpdate(cAgentData);
int ntimes = 20;
if (cAgentData.SenderWantsToWaitForRoot)
{
while (childAgentUpdate.IsChildAgent && ntimes-- > 0)
Thread.Sleep(1000);
m_log.DebugFormat(
"[SCENE]: Found presence {0} {1} {2} in {3} after {4} waits",
childAgentUpdate.Name, childAgentUpdate.UUID, childAgentUpdate.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 20 - ntimes);
if (childAgentUpdate.IsChildAgent)
return false;
}
return true; return true;
} }
return false; return false;
@ -4258,7 +4279,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name='agentID'></param> /// <param name='agentID'></param>
protected virtual ScenePresence WaitGetScenePresence(UUID agentID) protected virtual ScenePresence WaitGetScenePresence(UUID agentID)
{ {
int ntimes = 10; int ntimes = 20;
ScenePresence sp = null; ScenePresence sp = null;
while ((sp = GetScenePresence(agentID)) == null && (ntimes-- > 0)) while ((sp = GetScenePresence(agentID)) == null && (ntimes-- > 0))
Thread.Sleep(1000); Thread.Sleep(1000);
@ -4267,10 +4288,6 @@ namespace OpenSim.Region.Framework.Scenes
m_log.WarnFormat( m_log.WarnFormat(
"[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout", "[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout",
agentID, RegionInfo.RegionName); agentID, RegionInfo.RegionName);
// else
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits",
// sp.Name, sp.UUID, sp.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 10 - ntimes);
return sp; return sp;
} }
@ -5577,7 +5594,7 @@ namespace OpenSim.Region.Framework.Scenes
try try
{ {
if (!AuthorizeUser(aCircuit, out reason)) if (!AuthorizeUser(aCircuit, false, out reason))
{ {
// m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID); // m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID);
return false; return false;

View File

@ -29,7 +29,9 @@ using System;
using System.Xml; using System.Xml;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Timers; using System.Timers;
using Timer = System.Timers.Timer;
using OpenMetaverse; using OpenMetaverse;
using log4net; using log4net;
using Nini.Config; using Nini.Config;
@ -801,6 +803,7 @@ namespace OpenSim.Region.Framework.Scenes
{ {
ControllingClient.OnCompleteMovementToRegion += CompleteMovement; ControllingClient.OnCompleteMovementToRegion += CompleteMovement;
ControllingClient.OnAgentUpdate += HandleAgentUpdate; ControllingClient.OnAgentUpdate += HandleAgentUpdate;
ControllingClient.OnAgentCameraUpdate += HandleAgentCamerasUpdate;
ControllingClient.OnAgentRequestSit += HandleAgentRequestSit; ControllingClient.OnAgentRequestSit += HandleAgentRequestSit;
ControllingClient.OnAgentSit += HandleAgentSit; ControllingClient.OnAgentSit += HandleAgentSit;
ControllingClient.OnSetAlwaysRun += HandleSetAlwaysRun; ControllingClient.OnSetAlwaysRun += HandleSetAlwaysRun;
@ -1010,9 +1013,8 @@ namespace OpenSim.Region.Framework.Scenes
// recorded, which stops the input from being processed. // recorded, which stops the input from being processed.
MovementFlag = 0; MovementFlag = 0;
// DIVA NOTE: I moved TriggerOnMakeRootAgent out of here and into the end of m_scene.EventManager.TriggerOnMakeRootAgent(this);
// CompleteMovement. We don't want modules doing heavy computation before CompleteMovement
// is over.
} }
public int GetStateSource() public int GetStateSource()
@ -1124,7 +1126,26 @@ namespace OpenSim.Region.Framework.Scenes
public void StopFlying() public void StopFlying()
{ {
ControllingClient.StopFlying(this); Vector3 pos = AbsolutePosition;
if (Appearance.AvatarHeight != 127.0f)
pos += new Vector3(0f, 0f, (Appearance.AvatarHeight / 6f));
else
pos += new Vector3(0f, 0f, (1.56f / 6f));
AbsolutePosition = pos;
// attach a suitable collision plane regardless of the actual situation to force the LLClient to land.
// Collision plane below the avatar's position a 6th of the avatar's height is suitable.
// Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a
// certain amount.. because the LLClient wouldn't land in that situation anyway.
// why are we still testing for this really old height value default???
if (Appearance.AvatarHeight != 127.0f)
CollisionPlane = new Vector4(0, 0, 0, pos.Z - Appearance.AvatarHeight / 6f);
else
CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f / 6f));
ControllingClient.SendAgentTerseUpdate(this);
} }
/// <summary> /// <summary>
@ -1291,6 +1312,26 @@ namespace OpenSim.Region.Framework.Scenes
PhysicsActor.Size = new Vector3(0.45f, 0.6f, height); PhysicsActor.Size = new Vector3(0.45f, 0.6f, height);
} }
private bool WaitForUpdateAgent(IClientAPI client)
{
// Before UpdateAgent, m_originRegionID is UUID.Zero; after, it's non-Zero
int count = 20;
while (m_originRegionID.Equals(UUID.Zero) && count-- > 0)
{
m_log.DebugFormat("[SCENE PRESENCE]: Agent {0} waiting for update in {1}", client.Name, Scene.RegionInfo.RegionName);
Thread.Sleep(200);
}
if (m_originRegionID.Equals(UUID.Zero))
{
// Movement into region will fail
m_log.WarnFormat("[SCENE PRESENCE]: Update agent {0} never arrived", client.Name);
return false;
}
return true;
}
/// <summary> /// <summary>
/// Complete Avatar's movement into the region. /// Complete Avatar's movement into the region.
/// </summary> /// </summary>
@ -1308,6 +1349,13 @@ namespace OpenSim.Region.Framework.Scenes
"[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}", "[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}",
client.Name, Scene.RegionInfo.RegionName, AbsolutePosition); client.Name, Scene.RegionInfo.RegionName, AbsolutePosition);
// Make sure it's not a login agent. We don't want to wait for updates during login
if ((m_teleportFlags & TeleportFlags.ViaLogin) == 0)
// Let's wait until UpdateAgent (called by departing region) is done
if (!WaitForUpdateAgent(client))
// The sending region never sent the UpdateAgent data, we have to refuse
return;
Vector3 look = Velocity; Vector3 look = Velocity;
if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) if ((look.X == 0) && (look.Y == 0) && (look.Z == 0))
@ -1328,10 +1376,11 @@ namespace OpenSim.Region.Framework.Scenes
bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0);
MakeRootAgent(AbsolutePosition, flying); MakeRootAgent(AbsolutePosition, flying);
// Tell the client that we're totally ready
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
// Remember in HandleUseCircuitCode, we delayed this to here // Remember in HandleUseCircuitCode, we delayed this to here
// This will also send the initial data to clients when TP to a neighboring region.
// Not ideal, but until we know we're TP-ing from a neighboring region, there's not much we can do
if (m_teleportFlags > 0) if (m_teleportFlags > 0)
SendInitialDataToMe(); SendInitialDataToMe();
@ -1387,10 +1436,6 @@ namespace OpenSim.Region.Framework.Scenes
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms", // "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
// client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds); // client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds);
// DIVA NOTE: moved this here from MakeRoot. We don't want modules making heavy
// computations before CompleteMovement is over
m_scene.EventManager.TriggerOnMakeRootAgent(this);
} }
/// <summary> /// <summary>
@ -1448,10 +1493,6 @@ namespace OpenSim.Region.Framework.Scenes
return; return;
} }
++m_movementUpdateCount;
if (m_movementUpdateCount < 1)
m_movementUpdateCount = 1;
#region Sanity Checking #region Sanity Checking
// This is irritating. Really. // This is irritating. Really.
@ -1482,21 +1523,6 @@ namespace OpenSim.Region.Framework.Scenes
AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags; AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags;
// Camera location in world. We'll need to raytrace
// from this location from time to time.
CameraPosition = agentData.CameraCenter;
if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance)
{
ReprioritizeUpdates();
m_lastCameraPosition = CameraPosition;
}
// Use these three vectors to figure out what the agent is looking at
// Convert it to a Matrix and/or Quaternion
CameraAtAxis = agentData.CameraAtAxis;
CameraLeftAxis = agentData.CameraLeftAxis;
CameraUpAxis = agentData.CameraUpAxis;
// The Agent's Draw distance setting // The Agent's Draw distance setting
// When we get to the point of re-computing neighbors everytime this // When we get to the point of re-computing neighbors everytime this
// changes, then start using the agent's drawdistance rather than the // changes, then start using the agent's drawdistance rather than the
@ -1504,12 +1530,6 @@ namespace OpenSim.Region.Framework.Scenes
// DrawDistance = agentData.Far; // DrawDistance = agentData.Far;
DrawDistance = Scene.DefaultDrawDistance; DrawDistance = Scene.DefaultDrawDistance;
// Check if Client has camera in 'follow cam' or 'build' mode.
Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation);
m_followCamAuto = ((CameraUpAxis.Z > 0.959f && CameraUpAxis.Z < 0.98f)
&& (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false;
m_mouseLook = (flags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0; m_mouseLook = (flags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0;
m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0; m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0;
@ -1529,17 +1549,6 @@ namespace OpenSim.Region.Framework.Scenes
StandUp(); StandUp();
} }
//m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto);
// Raycast from the avatar's head to the camera to see if there's anything blocking the view
if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast())
{
if (m_followCamAuto)
{
Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT;
m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(CameraPosition - posAdjusted), Vector3.Distance(CameraPosition, posAdjusted) + 0.3f, RayCastCameraCallback);
}
}
uint flagsForScripts = (uint)flags; uint flagsForScripts = (uint)flags;
flags = RemoveIgnoredControls(flags, IgnoredControls); flags = RemoveIgnoredControls(flags, IgnoredControls);
@ -1763,7 +1772,76 @@ namespace OpenSim.Region.Framework.Scenes
SendControlsToScripts(flagsForScripts); SendControlsToScripts(flagsForScripts);
} }
// We need to send this back to the client in order to see the edit beams
if ((State & (uint)AgentState.Editing) != 0)
ControllingClient.SendAgentTerseUpdate(this);
m_scene.EventManager.TriggerOnClientMovement(this); m_scene.EventManager.TriggerOnClientMovement(this);
}
/// <summary>
/// This is the event handler for client cameras. If a client is moving, or moving the camera, this event is triggering.
/// </summary>
private void HandleAgentCamerasUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData)
{
//m_log.DebugFormat(
// "[SCENE PRESENCE]: In {0} received agent camera update from {1}, flags {2}",
// Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags);
if (IsChildAgent)
{
// // m_log.Debug("DEBUG: HandleAgentUpdate: child agent");
return;
}
++m_movementUpdateCount;
if (m_movementUpdateCount < 1)
m_movementUpdateCount = 1;
AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags;
// Camera location in world. We'll need to raytrace
// from this location from time to time.
CameraPosition = agentData.CameraCenter;
if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance)
{
ReprioritizeUpdates();
m_lastCameraPosition = CameraPosition;
}
// Use these three vectors to figure out what the agent is looking at
// Convert it to a Matrix and/or Quaternion
CameraAtAxis = agentData.CameraAtAxis;
CameraLeftAxis = agentData.CameraLeftAxis;
CameraUpAxis = agentData.CameraUpAxis;
// The Agent's Draw distance setting
// When we get to the point of re-computing neighbors everytime this
// changes, then start using the agent's drawdistance rather than the
// region's draw distance.
// DrawDistance = agentData.Far;
DrawDistance = Scene.DefaultDrawDistance;
// Check if Client has camera in 'follow cam' or 'build' mode.
Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation);
m_followCamAuto = ((CameraUpAxis.Z > 0.959f && CameraUpAxis.Z < 0.98f)
&& (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false;
//m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto);
// Raycast from the avatar's head to the camera to see if there's anything blocking the view
if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast())
{
if (m_followCamAuto)
{
Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT;
m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(CameraPosition - posAdjusted), Vector3.Distance(CameraPosition, posAdjusted) + 0.3f, RayCastCameraCallback);
}
}
TriggerScenePresenceUpdated(); TriggerScenePresenceUpdated();
} }

View File

@ -687,6 +687,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
public event Action<IClientAPI, bool> OnCompleteMovementToRegion; public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnPreAgentUpdate;
public event UpdateAgent OnAgentUpdate; public event UpdateAgent OnAgentUpdate;
public event UpdateAgent OnAgentCameraUpdate;
public event AgentRequestSit OnAgentRequestSit; public event AgentRequestSit OnAgentRequestSit;
public event AgentSit OnAgentSit; public event AgentSit OnAgentSit;
public event AvatarPickerRequest OnAvatarPickerRequest; public event AvatarPickerRequest OnAvatarPickerRequest;
@ -1672,7 +1673,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
{ {
} }
public void StopFlying(ISceneEntity presence) public void SendAgentTerseUpdate(ISceneEntity presence)
{ {
} }

View File

@ -611,7 +611,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
// //
if (showParams.Length <= 4) if (showParams.Length <= 4)
{ {
m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,6} {3,11} {4, 10}", "Region", "Name", "Root", "Time", "Reqs/min"); m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", "Region", "Name", "Root", "Time", "Reqs/min", "AgentUpdates");
foreach (Scene scene in m_scenes.Values) foreach (Scene scene in m_scenes.Values)
{ {
scene.ForEachClient( scene.ForEachClient(
@ -624,9 +624,15 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum(); int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum();
avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1);
m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,4} {3,9}min {4,10}", m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}",
scene.RegionInfo.RegionName, llClient.Name, scene.RegionInfo.RegionName, llClient.Name,
(llClient.SceneAgent.IsChildAgent ? "N" : "Y"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs); llClient.SceneAgent.IsChildAgent ? "N" : "Y",
(DateTime.Now - cinfo.StartedTime).Minutes,
avg_reqs,
string.Format(
"{0} ({1:0.00}%)",
llClient.TotalAgentUpdates,
(float)cinfo.SyncRequests["AgentUpdate"] / llClient.TotalAgentUpdates * 100));
} }
}); });
} }

View File

@ -258,6 +258,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
public event Action<IClientAPI, bool> OnCompleteMovementToRegion; public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnPreAgentUpdate;
public event UpdateAgent OnAgentUpdate; public event UpdateAgent OnAgentUpdate;
public event UpdateAgent OnAgentCameraUpdate;
public event AgentRequestSit OnAgentRequestSit; public event AgentRequestSit OnAgentRequestSit;
public event AgentSit OnAgentSit; public event AgentSit OnAgentSit;
public event AvatarPickerRequest OnAvatarPickerRequest; public event AvatarPickerRequest OnAvatarPickerRequest;
@ -1228,7 +1229,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{ {
} }
public void StopFlying(ISceneEntity presence) public void SendAgentTerseUpdate(ISceneEntity presence)
{ {
} }

View File

@ -130,6 +130,7 @@ public class BSActorAvatarMove : BSActor
SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */); SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */);
m_physicsScene.BeforeStep += Mover; m_physicsScene.BeforeStep += Mover;
m_controllingPrim.OnPreUpdateProperty += Process_OnPreUpdateProperty;
m_walkingUpStairs = 0; m_walkingUpStairs = 0;
} }
@ -139,6 +140,7 @@ public class BSActorAvatarMove : BSActor
{ {
if (m_velocityMotor != null) if (m_velocityMotor != null)
{ {
m_controllingPrim.OnPreUpdateProperty -= Process_OnPreUpdateProperty;
m_physicsScene.BeforeStep -= Mover; m_physicsScene.BeforeStep -= Mover;
m_velocityMotor = null; m_velocityMotor = null;
} }
@ -197,7 +199,7 @@ public class BSActorAvatarMove : BSActor
{ {
if (m_controllingPrim.Flying) if (m_controllingPrim.Flying)
{ {
// Flying and not collising and velocity nearly zero. // Flying and not colliding and velocity nearly zero.
m_controllingPrim.ZeroMotion(true /* inTaintTime */); m_controllingPrim.ZeroMotion(true /* inTaintTime */);
} }
} }
@ -266,6 +268,19 @@ public class BSActorAvatarMove : BSActor
} }
} }
// Called just as the property update is received from the physics engine.
// Do any mode necessary for avatar movement.
private void Process_OnPreUpdateProperty(ref EntityProperties entprop)
{
// Don't change position if standing on a stationary object.
if (m_controllingPrim.IsStationary)
{
entprop.Position = m_controllingPrim.RawPosition;
m_physicsScene.PE.SetTranslation(m_controllingPrim.PhysBody, entprop.Position, entprop.Rotation);
}
}
// Decide if the character is colliding with a low object and compute a force to pop the // Decide if the character is colliding with a low object and compute a force to pop the
// avatar up so it can walk up and over the low objects. // avatar up so it can walk up and over the low objects.
private OMV.Vector3 WalkUpStairs() private OMV.Vector3 WalkUpStairs()

View File

@ -709,10 +709,10 @@ public sealed class BSCharacter : BSPhysObject
// the world that things have changed. // the world that things have changed.
public override void UpdateProperties(EntityProperties entprop) public override void UpdateProperties(EntityProperties entprop)
{ {
// Don't change position if standing on a stationary object. // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator.
if (!IsStationary) TriggerPreUpdatePropertyAction(ref entprop);
RawPosition = entprop.Position;
RawPosition = entprop.Position;
RawOrientation = entprop.Rotation; RawOrientation = entprop.Rotation;
// Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar
@ -740,7 +740,7 @@ public sealed class BSCharacter : BSPhysObject
// Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
// base.RequestPhysicsterseUpdate(); // PhysScene.PostUpdate(this);
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity); LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity);

View File

@ -493,6 +493,22 @@ namespace OpenSim.Services.HypergridService
return null; return null;
} }
private XInventoryFolder GetCurrentOutfitXFolder(UUID userID)
{
XInventoryFolder root = GetRootXFolder(userID);
if (root == null)
return null;
XInventoryFolder[] folders = m_Database.GetFolders(
new string[] { "agentID", "type", "parentFolderID" },
new string[] { userID.ToString(), ((int)AssetType.CurrentOutfitFolder).ToString(), root.folderID.ToString() });
if (folders.Length == 0)
return null;
return folders[0];
}
private XInventoryFolder GetSuitcaseXFolder(UUID principalID) private XInventoryFolder GetSuitcaseXFolder(UUID principalID)
{ {
// Warp! Root folder for travelers // Warp! Root folder for travelers
@ -531,6 +547,7 @@ namespace OpenSim.Services.HypergridService
if (m_SuitcaseTrees.TryGetValue(principalID, out t)) if (m_SuitcaseTrees.TryGetValue(principalID, out t))
return t; return t;
// Get the tree of the suitcase folder
t = GetFolderTreeRecursive(folder); t = GetFolderTreeRecursive(folder);
m_SuitcaseTrees.AddOrUpdate(principalID, t, 5*60); // 5minutes m_SuitcaseTrees.AddOrUpdate(principalID, t, 5*60); // 5minutes
return t; return t;
@ -577,6 +594,9 @@ namespace OpenSim.Services.HypergridService
List<XInventoryFolder> tree = new List<XInventoryFolder>(); List<XInventoryFolder> tree = new List<XInventoryFolder>();
tree.Add(suitcase); // Warp! the tree is the real root folder plus the children of the suitcase folder tree.Add(suitcase); // Warp! the tree is the real root folder plus the children of the suitcase folder
tree.AddRange(GetFolderTree(principalID, suitcase.folderID)); tree.AddRange(GetFolderTree(principalID, suitcase.folderID));
// Also add the Current Outfit folder to the list of available folders
tree.Add(GetCurrentOutfitXFolder(principalID));
XInventoryFolder f = tree.Find(delegate(XInventoryFolder fl) XInventoryFolder f = tree.Find(delegate(XInventoryFolder fl)
{ {
if (fl.folderID == folderID) return true; if (fl.folderID == folderID) return true;

View File

@ -106,6 +106,7 @@ namespace OpenSim.Tests.Common.Mock
public event Action<IClientAPI, bool> OnCompleteMovementToRegion; public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnPreAgentUpdate;
public event UpdateAgent OnAgentUpdate; public event UpdateAgent OnAgentUpdate;
public event UpdateAgent OnAgentCameraUpdate;
public event AgentRequestSit OnAgentRequestSit; public event AgentRequestSit OnAgentRequestSit;
public event AgentSit OnAgentSit; public event AgentSit OnAgentSit;
public event AvatarPickerRequest OnAvatarPickerRequest; public event AvatarPickerRequest OnAvatarPickerRequest;
@ -1255,7 +1256,7 @@ namespace OpenSim.Tests.Common.Mock
{ {
} }
public void StopFlying(ISceneEntity presence) public void SendAgentTerseUpdate(ISceneEntity presence)
{ {
} }

View File

@ -86,6 +86,9 @@
;; from the selected region_info_source. ;; from the selected region_info_source.
allow_regionless = false allow_regionless = false
;; Allow child agents to see into the region even if their root counterpart isn't allowed in here
see_into_region = true
; Maximum number of position, rotation and scale changes for each prim that the simulator will store for later undos ; Maximum number of position, rotation and scale changes for each prim that the simulator will store for later undos
; Increasing this number will increase memory usage. ; Increasing this number will increase memory usage.
MaxPrimUndos = 20 MaxPrimUndos = 20