Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patches from Second Life Mobile (August 2024) #95

Conversation

AdamFrisby
Copy link
Contributor

This contains a large collection of fixes, improvements and changes from the Second Life Mobile project to date. It's from a forked internal version which has been focusing on the features needed by SL Mobile - so; avatars, appearance, inventory and so on.

Each commit has notes on what has changed, in addition, each commit (unfortunately) contains more than one change; so you'll want to see the full commit notes for each on what was changed.

Some notable highlights:

  • We've updated the _Packets_.cs to match the latest message template (I noticed OpenSim had a few custom additions relating to VarRegions and such, so we've kept those, and patched them back manually.), this hadn't been done in a long, long time - so there's quite a lot of small changes in here. Testing notes: OpenSim might want to check these changes are still okay upstream.
  • The old appearance code was just plain broken. This has been partially rewritten (see 6e7f3cc)
  • We've changed ObjectPrimitives from an InternalDictionary to a ConcurrentDictionary, this might be a bit of a breaking change downstream in places (sorry everyone!), however once we began profiling performance, we found the locking was consuming onerous amounts of CPU time, moving to a lockless collection improved things dramatically. At some point, for consistency, we'd like to update ObjectAvatars as well, but avatar updates are 1/100th the frequency, so not as much of a priority.
  • The Event Queue (bfacd45, dfdc246 and 9cb6e94) ... we found the old Event Queue code would spontaneously die, never recover and then begin doing weird things™ triggering lots of other symptoms. The code here is a bit of a blunt force trauma fix, but it does at least work reliably now. This improves teleports and agent crossings significantly, and also is critical to ensuring that group messages arrive properly. Note: There is probably a better way to implement these fixes, so is the one area I'm least confident in this changeset.
  • There's new functionality relating to legacy materials (these are now implemented in LibreMetaverse), support for group titles and changing the active group, handling of ObjectAnimation packets (used by AniMesh) and so on.
  • We've added the ability to determine if a linkset is complete, and we haven't lost a packet. That also includes avatars and their attachments. The new .ChildCount properties will tell you how many children the Simulator expects there to be; if you don't have that many, well, you can do a ObjectPropertiesRequest to hopefully get them redelivered. Note: children includes the self, so make sure to factor that in to your calculation. Returns -1 if the simulator never gave us that data. (This was documented here https://jira.secondlife.com/browse/SL-20635 but you can find the corresponding changes to the official viewer code here: secondlife/viewer@ce75d0e )

Full Change List

  • Adds new function to GridManager - GetGridRegion by handle.
  • GridManager requests now properly handle case sensitivity (i.e. all region lookups should be case invariant)
  • Adds bidirectional lookups to GridManager allowing handle lookups as well as name. This is cached.
  • Always cache groups, even if there is no event bound to them.
  • Fix two exceptions triggered by inventory requests; this also fixes a internal data corruption issue with inventory / outfit handling.
  • Adds two new overloads to the Login(...) method, which take LoginDataResponse and also key parts of it.
  • Initialise AgentManagerCamera with a drawdist of 32m instead of 128m. Better to start small, then increase later; so we can prioritise what we get first; otherwise it'll be too late, and we're already getting tons of far away objects we didn't want.
  • Increases the maximum data throttle from 0.5mbit to 1.5mbit; this matches the desktop viewer maximum.
  • Adds default handler for GenericMessage packets, so we can know what isn't implemented.
  • Logout reply timer can now be cancelled, such as if it is successful.
  • OnSimDisconnected event now has a "Was Connected" property, this'll be false if OnDisconnected is firing before the simulator connected in the first place.
  • Adds a reason for disconnection events in logs
  • Prevents an exception if a session is cancelled before it is fully connected with the _packet<In|Out>box objects.
  • Adds a FindSimulator overload which takes a handle rather than just an IPEndPoint
  • Region handshake sends 0x7 instead of 0x2 in flags. This (apparently) impacts how the simulator will prioritise object updates for the viewer, and ensures we get a more steady flow of objects at early login.
  • CHANGE TO DEFAULT BEHAVIOUR: Changes logging to show UTC time rather than time since startup, so logs can be more easily coordinated between various running apps. Time since startup is not a useful bit of information when coordinating with other logs (such as simulator logs).
  • Fixes a subtle bug with TextureEntryFace where the face bits is incorrect. This is because it only uses 32-bits, the official viewer uses 64-bits, and the overflow matters.
  • Adds a .Valid property to TextureEntryFace - sometimes this is created without a valid DefaultFaceEntry, and will throw exceptions if you actually try use anything in this class. This helper functional allows you to avoid exceptions when the data is bad.
  • Adds a .CurrentParcel to ParcelManager to help locate the parcel an agent is currently on.
  • Fixes the EventQueue - it sometimes randomly dies and never recovers.
  • EventQueue be checked periodically to make sure it's running. Sometimes it dies. This is a blunt fix (along with the previous commit), but is needed to ensure that event queue-y things keep working.
  • UPSTREAM BREAKING CHANGE: ObjectPrimitives now uses ConcurrentDictionary, why? Well, at large scale, locking so frequently actually results in a nasty performance issue.
  • Adds a 500ms sleep between connecting to a simulator and CompleteAgentMovement - there's a race condition within the simulator which sometimes results in a faulty login, this seems to help.
  • SetSeedCaps has an extra parameter, if the user's simulator hasn't changed, don't do anything.
  • Add a lookup which lets you turn full Object UUIDs into LocalID, while LibreMetaverse prefers localID when referencing primitives, the official viewer uses UUIDs - and several things actually work better this way. (For example, avatars can spontaneously change LocalID sometimes). This way we can follow those changes.
  • Improves the reliability of teleports, especially while the Event Queue is down.
  • Adds some functions to check if a message is a normal agent message, or a group message.
  • Adds new RelativePositionEstimate to complement RelativePosition - RelativePosition actually ends up drifting badly because it accumulates timing errors. RelativePositionEstimate instead calculates each time it is requested, using known velocity and last update time, and is far more accurate in practice. This actually should be considered for addition to Primitive as well.
  • Adds an override to ChatEventArgs.ToString to provide more information
  • Improves reliability of Group Messages
  • Teleports previously used GetGridRegion which is sometimes unreliable, if this fails, request the full map, once, then cache the result.
  • Adds AgentManager.LastPositionUpdate to tell you when the avatars position was last updated
  • Sync's Packets.cs with the message template file for the first time in 12 years. Preserves the OpenSim specific packets and additions.
  • DEFAULT_AGENT_UPDATE_INTERVAL is now configurable
  • INTERPOLATION_INTERVAL is now configurable
  • You can now disable periodic SEND_AGENT_UPDATES independently (SEND_AGENT_UPDATES_REGULARLY) of normal updates, such as in CompleteAgentMovement
  • You can disable TexturePipeline if you don't need it
  • Fixes a possible timing bug in AgentManagerMovement.TurnToward
  • Fixes avatar appearance baking, and appearance messages
  • You can now check how many attachments an avatar should have via Avatar.Attachments - this needs a Simulator change to support it, as it is a new message property in Avatar messages. (Note: SL Simulators since about May/April '24 have been sending this data in release channels, OpenSim won't yet I don't think.). This can be used for determining if an avatar is "completely loaded" from the network side.
  • Adds the ability to check how many children a primitive has, and should have. (Note this relies on a similar change as the Attachment data above, and is sent by official simulators since the same time, for OpenSim -- the first few bits of the ExtraData field will contain this number, and it is easy to add)
  • Adds Primitive.GetChildren which returns all known children of a primitive. Gotcha: ChildCount will count the self as well. If ChildCount != Primitive.GetChildren(...).Count()+1 -- then the linkset is incomplete, and you may want to do a properties request on it to get sent the missing children. Returns -1 if the data is unknown.
  • Groups: Fixes an error in AvatarGroupsReply where it would always send your own AvatarID instead of the target AvatarID
  • Adds ChildCount property to AvatarManager for determining the number of attachments that should be present
  • Improves reliability of AvatarAppearance messages
  • Adds AvatarID to AgentGroupDataUpdateMessage
  • Correctly deserializes legacy materials
  • Adds ObjectAnimation packet handler and events
  • Adds new methods for requesting legacy materials
  • Completes switchover of ObjectPrimitives from InternalDictionary to ConcurrentDictionary
  • For clients using OBJECT_TRACKING, this may be a performance regression in busy deployments.
  • Adds CRC support to object updates, and adds this to tracking
  • Changes isNewObject to isProbablyNew to more accurately describe the property
  • Improves InterpolationTimer methods (but we don't use it - so please test this if you do.)
  • Fix some bugs in LibreMetaverse such that we weren't receiving changes to the displayed group title:
  • Clear cached name, groupName in Avatar when an ObjectUpdate has new NameValues.
  • Some message paths didn't maintain group title, while others did. (Although I don't think that affects this implementation.)
  • In the proxy, do send an avatar update for group title changing to empty string.
  • ActivateGroup and RequestAgentDataUpdate commands from UI to app to proxy to sim.
  • Message back to UI OnAgentDataUpdate, so that we can tell whether a given group of this agent is already active.

AdamFrisby and others added 25 commits August 2, 2024 02:55
* GridManager requests now properly handle case sensitivity (i.e. all region lookups should be case invariant)
* Adds bidirectional lookups to GridManager allowing handle lookups as well as name. Caches.
…m. Better to start small, then increase later; so we can prioritise what we get first; otherwise it'll be too late, and we're already getting tons of far away objects we didn't want.
…. If you're living in the same data center as the simulators, this can go faster.
…t isn't implemented.

* Logout reply timer can now be cancelled, such as if it is successful.
* OnSimDisconnected event now has a "Was Connected" property, this'll be false if OnDisconnected is firing before the simulator connected in the first place.
* Adds a reason for disconnection events in logs
* Prevents an exception if a session is cancelled before it is fully connected with the _packet?box objects.
* Adds a FindSimulator overload which takes a handle rather than just an IPEndPoint
* Region handshake sends 0x7 instead of 0x2 in flags. This impacts the order and speed of object loading.
…nated between various running apps. Time since startup is not a useful bit of information when coordinating with simulator and client logs.
…orrect. This is because it only uses 32-bits, the official viewer uses 64-bits, and the overflow matters.

* Adds a .Valid property to TextureEntryFace - sometimes it is not, and will throw exceptions if you actually try use anything in this class. At least have the ability to check first.
* I'm not a big fan of this code to be perfectly honest. It probably can be fixed better. There's possibly an error on the simulator side, or in this code, that I haven't managed to locate, when the EventQueue has an early return rather than long-polling as it should, and it's triggered by this code. Not sure if the official viewer suffers the same fault (possibly.)
… scale, locking so frequently actually results in a nasty performance issue.

* EventQueue be checked periodically to make sure it's running. Sometimes it dies. This is a blunt fix (along with the previous commit), but is needed to ensure that event queue-y things keep working.
* Adds a 500ms sleep between connecting to a simulator and CompleteAgentMovement - there's a race condition within the simulator which sometimes results in a faulty login, this seems to help.
* SetSeedCaps has an extra parameter, if the user's simulator hasn't changed, don't do anything.
* Add a lookup which lets you turn full Object UUIDs into LocalID, while LibreMetaverse prefers localID when referencing primitives, the official viewer uses UUIDs - and several things actually work better this way. (For example, avatars can spontaneously change LocalID sometimes). This way we can follow those changes.
…ueue is down.

* Adds some functions to check if a message is a normal agent message, or a group message.
* Adds new RelativePositionEstimate to complement RelativePosition - RelativePosition actually ends up drifting badly. RelativePositionEstimate instead calculates each time it is requested, using known velocity, and is far more accurate in practice.
* Adds an override to ChatEventArgs.ToString to dump information about the chat.
* Improves reliability of Group Messages
* Teleports previously used GetGridRegion which is sometimes unreliable, request the full map, once, if it fails. The results will be cached.
* Adds AgentManager.LastPositionUpdate to tell you when the avatars position was last updated
…me in 12 years. Preserves the OpenSim specific packets and additions.
* DEFAULT_AGENT_UPDATE_INTERVAL is now configurable
* INTERPOLATION_INTERVAL is now configurable
* You can now disable periodic SEND_AGENT_UPDATES independently (SEND_AGENT_UPDATES_REGULARLY) of normal updates, such as in CompleteAgentMovement
* You can disable TexturePipeline if you don't need it
* Fixes a possible timing bug in AgentManagerMovement.TurnToward
…atar.Attachments - this needs a Simulator change to support it, as it is a new message property in Avatar messages.

* This can be used for determining if an avatar is "completely loaded" from the network side, along with the next commit...
* Adds the ability to check how many children a primitive has, and should have.
* Adds Primitive.GetChildren which returns all known children of a primitive.
* Known Gotcha: ChildCount will count the self as well.
* If ChildCount != Primitive.GetChildren(...).Count()+1 -- then the linkset is incomplete, and you may want to do a properties request on it to get sent the missing children.
…end your own AvatarID instead of the target AvatarID

* Adds ChildCount property to AvatarManager for determining the number of attachments that should be present
* Improves reliability of AvatarAppearance messages
* Correctly deserializes legacy materials
* Adds new methods for requesting legacy materials
* Completes switchover of ObjectPrimitives from InternalDictionary to ConcurrentDictionary
* For clients using OBJECT_TRACKING, this may be a performance regression in busy deployments.
* Adds CRC support to object updates, and adds this to tracking
* Changes isNewObject to isProbablyNew to more accurately describe the property
* Improves InterpolationTimer methods (but we don't use it - so please test this if you do.)
…ges to the displayed group title:

* Clear cached name, groupName in Avatar when an ObjectUpdate has new NameValues.
* Some message paths didn't maintain group title, while others did. (Although I don't think that affects this implementation.)
* In the proxy, do send an avatar update for group title changing to empty string.
* ActivateGroup and RequestAgentDataUpdate commands from UI to app to proxy to sim.
* Message back to UI OnAgentDataUpdate, so that we can tell whether a given group of this agent is already active.
…taUpdateMessage messages

* Fixes XML comments
Client.Network.SendPacket(move, simulator);
Logger.Log("Sending complete agent movement to " + simulator.Handle + " / " + simulator.Name, Helpers.LogLevel.Info, Client);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer string interpolation in future commits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, Resharper usually hits me over the head about that one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Count it fixed. ;)

@AdamFrisby
Copy link
Contributor Author

Just had a review of the Codacy report, it's a bit pedantic, eh?

I have fixed a few of the things it has pointed out, but I'll argue against the remainder of its suggestions.

  • We've exposed AllCapabilities - this is because we use it elsewhere as a consumer of the library. Making it read-only would prevent someone downstream from adding new caps in the future, which is critical. (e.g. we use the new voice caps as part of the upcoming WebRTC voice) which are not yet in the library.
  • GetGridRegion is already in existence, I agree it should be called TryGetGridRegion, but this is an existing method, so not sure renaming it is the best course, since it'd break downstream code. Technically we added a new overload here, and the new overload could be in a better pattern, but I think that's overall worse.
  • Avatar.HoverHeight - this mirrors surrounding code structure, so I'm going to leave it.
  • There's a few complaints about encapsulating methods in properties, it's not terribly hard to do, but in most of these places feels pointless and creates redundant operations for simple return values. Will do it if asked.


if (responseString.Contains("502 Proxy Error"))
{
// LL's "go ask again" message.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof, always wondered. Assumed it was a squid error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's weird. I asked one the Simulator team about it, and they said it's haunted, and they'd love to replace it with something better.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OMV crashes on it. Used to be a big issue because of all the bots constantly crashing and logging back in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the issue is actually a little deeper. The problem is after this happens, it no longer "long polls" the HTTP request. The server team doesn't want an instant retry, so we kind of switch to 'active polling' here afterwards (which is my biggest issue with this code), which'll poll again in seconds. One of the subtle bugs we fixed in this version is to keep incrementing the message counter when this happens, and not sending the "isDone"=true, which keeps it somewhat working when it reaches this state.

We've added one more thing here which is the .RestartIfDead() method, this way if you're expecting an important message, like for example, teleports, or such, you can at least force an instant 'check for new messages'; rather than wait for the next natural reconnect.

This is almost certainly a Simulator bug, but as I said, code's haunted. (The Simulator dev might not have used those exact words, I think they actually said 'It's old, flaky and unreliable; and we want to replace it.')

@cinderblocks
Copy link
Owner

C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(115,10): error CS1026: ) expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(115,10): error CS1002: ; expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(117,9): error CS0106: The modifier 'public' is not valid for this item [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(445,2): error CS1513: } expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]

@AdamFrisby
Copy link
Contributor Author

C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(115,10): error CS1026: ) expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(115,10): error CS1002: ; expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(117,9): error CS0106: The modifier 'public' is not valid for this item [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]
C:\projects\libremetaverse-ksbcr\LibreMetaverse\Capabilities\EventQueueClient.cs(445,2): error CS1513: } expected [C:\projects\libremetaverse-ksbcr\LibreMetaverse\LibreMetaverse.csproj]

Argh, one moment. That was probably me when fixing the merge errors.

@AdamFrisby
Copy link
Contributor Author

Sorry, I did something really dumb, and edited our internal fork, instead of the external repo. This is going to take me about 15-30 minutes to fix. (The sooner we can drop the internal one, the better...)

@cinderblocks
Copy link
Owner

No worries. I have a prior engagement I need to get to so I will review when I get back.

@AdamFrisby
Copy link
Contributor Author

Okay, so - it's compiling now. So that's a win. TestClient needs some further updating, which I am doing now.

I'm a little bit more scared now - mainly by f394c71 (the sync with HEAD) which appears to have done quite a lot I didn't want it to.

Going to do a bit more review before I'm happy with this - I'll signal when to restart the review.

@AdamFrisby
Copy link
Contributor Author

Okay, I THINK it's okay now. We'll do some testing on our side with this version, and just verify that we've not functionally broken anything.

@AdamFrisby
Copy link
Contributor Author

Sorry, spoke too soon -- still got some issues. This might take me into early next week to iron out. I'll ping when we're ready for another retry! Enjoy the weekend!

@wleader
Copy link

wleader commented Aug 18, 2024

  • GetGridRegion is already in existence, I agree it should be called TryGetGridRegion, but this is an existing method, so not sure renaming it is the best course, since it'd break downstream code. Technically we added a new overload here, and the new overload could be in a better pattern, but I think that's overall worse.

This is entirely just my opinion, but....
As a user of LibreMetaverse, and a library package maintainer, and a long time developer, I am 100% in favor of breaking changes that improve the library. The only reason not to do it is because it creates some work for users of the library, but if a break is well documented, its almost never a big deal. It probably makes the consumer code better too.

The people that do complain about a breaking changes are almost always just trying to offload the maintenance and improvement work they should be doing on their products onto the package maintainers. I see it as exploitative and no package maintainer should be obligated to take on the extra work of maintaining backwards compatibility cruft onto themselves. So unless there is some kind of paid support contract to maintain something, It is almost always better for the package to break stuff.

@cinderblocks
Copy link
Owner

Many, not all, changes have been cherry-picked into master branch. Didn't make the ObjectPrimatives change thus far. Still considering the impact.

It would be advisable to sync the changes before continuing.

Thank you!

@AdamFrisby
Copy link
Contributor Author

Oh brilliant thankyou!

Sorry we went a little quiet, I was actually getting our team to do the same thing -- break each change up so merging would have been easier.

In terms of the signature change; we're about to do the work to change the local indexes to uuid -- maybe it makes the most sense to simply wait for that, and we'll close this PR.

@cinderblocks
Copy link
Owner

Agreed. Looking forward to the progress!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants