Region crossings: tune the handoff (neighbour child agents, border zone, crossing smoothness) #850

Closed
opened 2026-10-18 23:04:09 +00:00 by sky_line · 2 comments
Owner

Make region crossings (walking, flying, vehicles, and teleports to a neighbour) as close to seamless as the protocol allows.

Background

Near a border, neighbouring regions are supposed to already know your avatar as a child agent:

  • the viewer is connected to them;
  • they stream their objects and avatars to you;
  • the handoff when you cross is meant to be a hand-over between two regions that already know you, not a cold connect.

If we aren't establishing or keeping that state the way the simulators expect, crossings pay for setup at the worst moment. Examples: connecting late, our network draw distance pruning a neighbour we're about to enter, a missing or late AgentUpdate to the neighbour, or a slow CompleteAgentMovement after CrossedRegion.

Investigate

  • Protocol and current behaviour: how the simulator expects the border handoff to work (child agents, EnableSimulator / EstablishAgentCommunication, CrossedRegion, CompleteAgentMovement, agent updates to neighbours, camera / draw distance rules), and how our viewer behaves today, including the region ring and network draw distance, and the crossing-keep logic.
  • Where time goes in a crossing, measured end to end on foot, flying and in a vehicle.
  • Fixes: anything we do that defeats the pre-connected neighbour state.

Done when

  • The handoff is understood and documented.
  • Concrete fixes are filed or landed.
  • Crossing timing is measured before and after.
Make region crossings (walking, flying, vehicles, and teleports to a neighbour) as close to seamless as the protocol allows. ## Background Near a border, neighbouring regions are supposed to already know your avatar as a **child agent**: - the viewer is connected to them; - they stream their objects and avatars to you; - the handoff when you cross is meant to be a hand-over between two regions that already know you, not a cold connect. If we aren't establishing or keeping that state the way the simulators expect, crossings pay for setup at the worst moment. Examples: connecting late, our network draw distance pruning a neighbour we're about to enter, a missing or late `AgentUpdate` to the neighbour, or a slow `CompleteAgentMovement` after `CrossedRegion`. ## Investigate - **Protocol and current behaviour:** how the simulator expects the border handoff to work (child agents, `EnableSimulator` / `EstablishAgentCommunication`, `CrossedRegion`, `CompleteAgentMovement`, agent updates to neighbours, camera / draw distance rules), and how our viewer behaves today, including the region ring and network draw distance, and the crossing-keep logic. - **Where time goes in a crossing,** measured end to end on foot, flying and in a vehicle. - **Fixes:** anything we do that defeats the pre-connected neighbour state. ## Done when - [ ] The handoff is understood and documented. - [ ] Concrete fixes are filed or landed. - [ ] Crossing timing is measured before and after.
sky_line added this to the Slipstream project 2026-10-18 23:04:09 +00:00
Author
Owner

Handoff investigation (code read, 2026-10-18)

Short answer: mostly yes, the core handoff matches what the simulators expect. There is no "we drop the region we're about to enter" bug in the default setup. Five places weaken or can break it; fixes ranked below. No crossing has been captured in a log yet, so timings are still to be measured.

What we already do right

  • Neighbours connect at once. The agent's region, the region being entered (the anchor) and their 8 neighbours always connect as soon as EnableSimulator arrives. They skip the ring cap, pacing and draw-distance rules.
  • AgentUpdate and AgentThrottle go to the root region only. This is correct. Child agents get camera, Far and throttles from the root simulator, not from the viewer. Sources: OpenSim EnableChildAgents / ChildAgentDataUpdate, and the SL wiki "Adding neighbor region sequence" page, where the viewer keeps sending AgentUpdate to the host.
  • On CrossedRegion we send CompleteAgentMovement first thing. We keep the existing region object, and a duplicate seed cap doesn't reset caps or the event queue.
  • Nothing is gated before CompleteAgentMovement / AgentMovementComplete. The origin-shift frame is no longer O(N) (#568). Live neighbours are only dropped when they're dead.
  • Border-zone vehicles. An object is simulated by one region at a time; it is frozen and handed over at the border, with no dual simulation. What the overlap gives us is the neighbour's objects and avatars already streamed, plus a live circuit and caps.

Where we weaken or can break the handoff

  1. Fast-movement network draw distance floor (192 m above 25 m/s). The simulator only creates the destination's child agent once it's within Far. At 192 m that leaves about 7.7 s at 25 m/s and about 3.2 s at 60 m/s to connect, handshake, stream objects and get caps; diagonal neighbours get less. Far also drops at once when fast mode starts, so on SL regions we already had can be disabled and then re-streamed as we approach.
  2. A neighbour's textures wait for that region's own caps. Texture fetch retries every 1 s until EstablishAgentCommunication and the seed cap arrive; the SL wiki notes EstablishAgentCommunication sometimes arrives about a minute late. The asset storage path also suspends all generic asset requests (animations, sounds) while the agent region's caps are pending, even though the previous asset URL still works. The mesh path already falls back to the previous URL.
  3. CrossedRegion never sets up a circuit for the destination. The teleport path sends UseCircuitCode; the crossing path doesn't. If the destination isn't connected, CompleteAgentMovement is dropped as "unknown circuit" and the crossing hangs; OpenSim waits 30 s for our UseCircuitCode, then fails the crossing. Moving the anchor also discards the destination's stashed host and seed. It can happen in two ways:
    • SlipstreamMaxRegionRing=0: the ring check runs before the core exemption, so neighbours are never connected. Debug setting only, and its comment ("crossings then connect only at the border") is wrong.
    • A neighbour silent for 30 s can be stale-dropped. Only the root, anchor and recent root are exempt; the other 8 neighbours aren't.
  4. Regions two away from the old root connect on the crossing frame. If they were stashed (beyond render draw distance, region cap or pacing), they connect when the anchor moves. The region cap can be filled by the trail of regions behind us, and the pacer and region idle budget have no direction-of-travel bias.
  5. CrossingKeep grace vs. the server freeze. The near-edge grace is 4 s, but crossing freezes run about 0.5–10 s. An avatar whose update arrives late is killed and re-created; attachments later than the 20 s grace are too. This is the "re-created 10–30 s after the crossing" signature, and CrossingKeep expired= already counts it.

Fixes, ranked by impact and risk

  1. Lead-time-based fast Far. Use clamp(speed × ~8 s + 64, 256, SLNetDrawDistanceMax) and ramp it down instead of snapping. High impact for vehicles and fast flight, low–medium risk; the 25-region cap still bounds the connected set.
  2. ViewerAsset URL fallback. For textures and generic assets, use the agent region's or the last good ViewerAsset URL when the object's region has no caps yet. Medium–high impact, low risk.
  3. Make sure the destination circuit exists on CrossedRegion. If it doesn't, call connectSimulator before CompleteAgentMovement. Keep the stashed seed instead of discarding it, and log each case as "destination not pre-connected". Low effort, low risk.
  4. Exempt neighbours from ring 0 and stale drops. Check the core exemption before the ring check, or clamp the setting to at least 1, and fix the comment. Don't stale-drop the 8 neighbours, or wait twice as long. Trivial.
  5. Direction-aware pacer and idle budget. Look ahead along velocity to pick which stashed region to connect and which region gets more idle budget, and prefetch the object cache read when a region enters the stash. Medium impact at speed, low risk.
  6. Tune CrossingKeep from data. Extend the grace while the object is still crossing an edge into a connected region, or while Crossing568 still reports the attachment missing. Low risk.

Measurement plan

Extend Crossing568 rather than adding a new tag. At CrossedRegion, add:

  • dest_pre=: how long the destination has been connected, whether the handshake reply was sent, whether caps and the event queue are up, or NOT_CONNECTED;
  • far_sent=, speed= and the fast-mode flag;
  • how many regions were connected on the crossing;
  • first_full_frame= after AgentMovementComplete.

These existing logs already help: control_restored, attachment arrival times, frame timings, the keep(#607) line, the CrossingKeep counters, #RegionRing connect/drop lines and #NetDrawDistance Far changes.

Test runs: the same border, at least 5 crossings per case, before and after each fix. Cases are walking, normal flight, boosted flight, a land vehicle at about 20–30 m/s, and a boat or aircraft at about 40–60 m/s including a corner crossing. A/B SLNetDrawDistanceFlightMin at 192, at 0 and with the lead-time rule.

Done when:

  • dest_pre is always connected with caps;
  • no regions connect on the crossing frame when walking or in normal flight;
  • re-created avatars=0 in steady crossings.
## Handoff investigation (code read, 2026-10-18) **Short answer:** mostly yes, the core handoff matches what the simulators expect. There is no "we drop the region we're about to enter" bug in the default setup. Five places weaken or can break it; fixes ranked below. No crossing has been captured in a log yet, so timings are still to be measured. ### What we already do right - **Neighbours connect at once.** The agent's region, the region being entered (the anchor) and their 8 neighbours always connect as soon as `EnableSimulator` arrives. They skip the ring cap, pacing and draw-distance rules. - **AgentUpdate and AgentThrottle go to the root region only. This is correct.** Child agents get camera, Far and throttles from the root simulator, not from the viewer. Sources: OpenSim `EnableChildAgents` / `ChildAgentDataUpdate`, and the SL wiki "Adding neighbor region sequence" page, where the viewer keeps sending AgentUpdate to the host. - **On `CrossedRegion` we send `CompleteAgentMovement` first thing.** We keep the existing region object, and a duplicate seed cap doesn't reset caps or the event queue. - **Nothing is gated before `CompleteAgentMovement` / `AgentMovementComplete`.** The origin-shift frame is no longer O(N) (#568). Live neighbours are only dropped when they're dead. - **Border-zone vehicles.** An object is simulated by one region at a time; it is frozen and handed over at the border, with no dual simulation. What the overlap gives us is the neighbour's objects and avatars already streamed, plus a live circuit and caps. ### Where we weaken or can break the handoff 1. **Fast-movement network draw distance floor (192 m above 25 m/s).** The simulator only creates the destination's child agent once it's within Far. At 192 m that leaves about 7.7 s at 25 m/s and about 3.2 s at 60 m/s to connect, handshake, stream objects and get caps; diagonal neighbours get less. Far also drops at once when fast mode starts, so on SL regions we already had can be disabled and then re-streamed as we approach. 2. **A neighbour's textures wait for that region's own caps.** Texture fetch retries every 1 s until `EstablishAgentCommunication` and the seed cap arrive; the SL wiki notes `EstablishAgentCommunication` sometimes arrives about a minute late. The asset storage path also suspends all generic asset requests (animations, sounds) while the agent region's caps are pending, even though the previous asset URL still works. The mesh path already falls back to the previous URL. 3. **`CrossedRegion` never sets up a circuit for the destination.** The teleport path sends `UseCircuitCode`; the crossing path doesn't. If the destination isn't connected, `CompleteAgentMovement` is dropped as "unknown circuit" and the crossing hangs; OpenSim waits 30 s for our `UseCircuitCode`, then fails the crossing. Moving the anchor also discards the destination's stashed host and seed. It can happen in two ways: - `SlipstreamMaxRegionRing=0`: the ring check runs before the core exemption, so neighbours are never connected. Debug setting only, and its comment ("crossings then connect only at the border") is wrong. - A neighbour silent for 30 s can be stale-dropped. Only the root, anchor and recent root are exempt; the other 8 neighbours aren't. 4. **Regions two away from the old root connect on the crossing frame.** If they were stashed (beyond render draw distance, region cap or pacing), they connect when the anchor moves. The region cap can be filled by the trail of regions behind us, and the pacer and region idle budget have no direction-of-travel bias. 5. **CrossingKeep grace vs. the server freeze.** The near-edge grace is 4 s, but crossing freezes run about 0.5–10 s. An avatar whose update arrives late is killed and re-created; attachments later than the 20 s grace are too. This is the "re-created 10–30 s after the crossing" signature, and `CrossingKeep` `expired=` already counts it. ### Fixes, ranked by impact and risk 1. **Lead-time-based fast Far.** Use `clamp(speed × ~8 s + 64, 256, SLNetDrawDistanceMax)` and ramp it down instead of snapping. High impact for vehicles and fast flight, low–medium risk; the 25-region cap still bounds the connected set. 2. **ViewerAsset URL fallback.** For textures and generic assets, use the agent region's or the last good ViewerAsset URL when the object's region has no caps yet. Medium–high impact, low risk. 3. **Make sure the destination circuit exists on `CrossedRegion`.** If it doesn't, call `connectSimulator` before `CompleteAgentMovement`. Keep the stashed seed instead of discarding it, and log each case as "destination not pre-connected". Low effort, low risk. 4. **Exempt neighbours from ring 0 and stale drops.** Check the core exemption before the ring check, or clamp the setting to at least 1, and fix the comment. Don't stale-drop the 8 neighbours, or wait twice as long. Trivial. 5. **Direction-aware pacer and idle budget.** Look ahead along velocity to pick which stashed region to connect and which region gets more idle budget, and prefetch the object cache read when a region enters the stash. Medium impact at speed, low risk. 6. **Tune CrossingKeep from data.** Extend the grace while the object is still crossing an edge into a connected region, or while Crossing568 still reports the attachment missing. Low risk. ### Measurement plan Extend `Crossing568` rather than adding a new tag. At `CrossedRegion`, add: - `dest_pre=`: how long the destination has been connected, whether the handshake reply was sent, whether caps and the event queue are up, or `NOT_CONNECTED`; - `far_sent=`, `speed=` and the fast-mode flag; - how many regions were connected on the crossing; - `first_full_frame=` after `AgentMovementComplete`. These existing logs already help: `control_restored`, attachment arrival times, frame timings, the `keep(#607)` line, the `CrossingKeep` counters, `#RegionRing` connect/drop lines and `#NetDrawDistance` Far changes. **Test runs:** the same border, at least 5 crossings per case, before and after each fix. Cases are walking, normal flight, boosted flight, a land vehicle at about 20–30 m/s, and a boat or aircraft at about 40–60 m/s including a corner crossing. A/B `SLNetDrawDistanceFlightMin` at 192, at 0 and with the lead-time rule. **Done when:** - `dest_pre` is always connected with caps; - no regions connect on the crossing frame when walking or in normal flight; - `re-created avatars=0` in steady crossings.
Author
Owner

Shipped in 1.4.0 Beta 1. Closing; reopen if it misbehaves.

Shipped in 1.4.0 Beta 1. Closing; reopen if it misbehaves.
sky_line 2026-10-20 15:03:20 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#850
No description provided.