OpenSimulator Internals/Walkthroughs/Avatar Transfer Between Regions
OpenSimulator Internals/Walkthroughs/Avatar Transfer Between Regions
[edit]Overview
[edit]Traces what happens in code, connectors, and database when an avatar teleports from one region to another on the same grid. Covers the V2 protocol path (protocol version >= 0.2), which is the current standard.
For region border crossing (walking/flying across a boundary), see the Crossing section below. For HG teleport, see OpenSimulator Internals/Walkthroughs/Avatar Goes HG.
Part 1: Teleport
[edit]1. Teleport Requested
[edit]OpenSim/Region/Framework/Scenes/Scene.cs -- RequestTeleportLocation() OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs -- Teleport()
- Viewer sends TeleportLocationRequest packet (or map click, lure, landmark).
- Scene.RequestTeleportLocation() gets ScenePresence, checks not deleted/in transit, calls EntityTransferModule.Teleport().
- EntityTransferModule.Teleport():
- Checks CanTeleport permissions.
- Calls SetInTransit() -- rejects if already in transit.
- Calls Util.CompareRegionHandles() to determine if destination is within current region bounds.
- If same region: calls TeleportAgentWithinRegion() -- local move, no inter-region protocol.
- If different region: calls TeleportAgentToDifferentRegion().
2. Destination Lookup
[edit]EntityTransferModule -- TeleportAgentToDifferentRegion() → GetTeleportDestinationRegion()
- Calls GridService.GetRegionByPosition() -- HTTP GET to ROBUST GridService.
- ROBUST reads regions table, returns GridRegion for the destination.
- Calls GetFinalDestination() -- on base class returns same region (HG override resolves foreign address).
- Calls ValidateGenericConditions() -- base class returns true (override point).
- DB reads (on ROBUST side)
- regions -- SELECT by world position
3. Pre-flight Checks
[edit]EntityTransferModule -- DoTeleportInternal()
- DNS resolution: finalDestination.ExternalEndPoint.
- Calls SimulationService.QueryAccess() -- HTTP POST to destination simulator's QueryAccess endpoint.
- Destination calls Scene.QueryAccess(): checks ban list, parcel access, agent limit, AllowAvatarCrossing.
- Returns true/false + reason string.
- Checks sp.Appearance.CanTeleport(ctx.OutboundVersion) -- rejects if outfit incompatible with destination protocol version.
- If avatar sitting: calls sp.StandUp().
- Sets sp.IsInLocalTransit, sp.IsInTransit = true.
- Sends TeleportStart to viewer.
4. Create Agent at Destination (V2)
[edit]EntityTransferModule -- TransferAgent_V2() → CreateAgent()
- Builds AgentCircuitData for the destination -- includes caps path, start position, appearance height.
- Determines OutSideViewRange: whether destination is outside current draw distance (needs new child agent vs reusing existing one).
- Calls SimulationService.CreateAgent() -- HTTP POST to destination simulator's NewAgentIn endpoint.
- Destination calls Scene.NewUserConnection() with the AgentCircuitData.
- NewUserConnection() validates presence, authorises user, sets up caps, adds circuit.
- Returns success/failure + reason.
- If failed: sends TeleportFailed to viewer, clears transit state.
- Fires EventManager.TriggerTeleportStart on source.
5. Send TeleportFinish to Viewer
[edit]EntityTransferModule -- TransferAgent_V2()
- Sets sp.IsChildAgent = true on source (before UpdateAgent, to prevent race with destination requesting a child close).
- Sends TeleportFinishEvent via IEventQueue -- gives viewer: destination handle, endpoint, caps seed URL, teleport flags.
- Viewer receives TeleportFinish and begins connecting to destination simulator.
6. Send Full Agent State to Destination
[edit]EntityTransferModule -- TransferAgent_V2() → UpdateAgent() ScenePresence -- CopyTo()
- Builds AgentData via sp.CopyTo(agent, isCrossUpdate=false):
- Position, velocity, camera axes.
- Appearance (full AvatarAppearance copy).
- Animations, animation overrides, motion state.
- Attachment SOGs (cloned via AttachmentsModule.CopyAttachments()) with script states.
- Script controls, god data, group info, friends online cache.
- Sets SenderWantsToWaitForRoot = true.
- Calls SimulationService.UpdateAgent() -- HTTP POST to destination simulator.
- Destination calls Scene.IncomingUpdateChildAgent(AgentData).
- Destination's ScenePresence.UpdateChildAgent() calls CopyFrom() which sets m_originRegionID.
- If SenderWantsToWaitForRoot: destination waits (up to 25s polling) for sp.IsChildAgent to become false (CompleteMovement fires when viewer sends CompleteMovementToRegion).
- Returns true when avatar is root at destination.
- UpdateAgent() blocks on source until destination returns.
7. Avatar Becomes Root at Destination
[edit]ScenePresence -- CompleteMovement() (destination side)
- Viewer sends CompleteMovementToRegion to destination simulator.
- Destination CompleteMovement() fires (see OpenSimulator Internals/Walkthroughs/Avatar Rez In Region steps 3-5 for detail).
- Key difference from login: WaitForUpdateAgent() waits up to 10s for m_originRegionID to be set (set by UpdateAgent arriving). For teleport this is not skipped.
- MakeRootAgent() called -- physics actor created, position adjusted.
- Avatar data sent to all presences at destination.
- Attachments: for teleport (not IsRealLogin), attachments were carried in AgentData. Scripts restarted via RestartAttachmentScripts() rather than RezAttachments().
8. Source Cleans Up
[edit]EntityTransferModule -- TransferAgent_V2() (source, after UpdateAgent returns)
- Calls sp.HasMovedAway(nearRegion) -- if nearRegion (within view range and not logout): calls AttachmentsModule.DeleteAttachmentsFromScene() for non-HUD attachments; sends kill packets to presences that won't see the avatar in the new region.
- Calls sp.MakeChildAgent(destinationHandle) -- source presence becomes child. Physics removed. OnMakeChildAgent fires.
- Closes child agents: sp.CloseChildAgents(childRegionsToClose) or sp.closeAllChildAgents() for logout=true.
- If NeedsClosing (OutSideViewRange): waits up to 15s for sp.IsInTransit to clear, then calls m_scene.CloseAgent() -- fully removes source presence.
- If within view range (not NeedsClosing): source keeps a child agent. sp.IsInTransit = false.
- No DB writes at source during teleport. Attachment saves happen at logout/detach, not on teleport.
9. GridUser Updated at Destination
[edit]After CompleteMovement() completes at destination, the region updates position:
- GridUserService.SetLastPosition() -- updates griduser table with new RegionID, position, look.
- DB writes
- griduser -- UPDATE: LastRegionID, LastPosition, LastLookAt
10. PresenceService Updated
[edit]- PresenceService.ReportAgent() (or equivalent) -- updates presence table with new RegionID.
- DB writes
- presence -- UPDATE: RegionID to destination region
Part 2: Region Border Crossing
[edit]Border crossing is initiated by the source region's physics simulation, not the viewer.
1. Border Detected
[edit]ScenePresence -- CheckForBorderCrossing() (called each heartbeat frame from Update())
- Checks not IsChildAgent, not IsInTransit, not sitting, PhysicsActor != null.
- Projects position: pos + velocity * FrameTime.
- If projected position is outside region bounds: calls Scene.CrossAgentToNewRegion().
- Scene.CrossAgentToNewRegion() checks AllowAvatarCrossing flag, then calls EntityTransferModule.Cross().
2. Destination Found
[edit]EntityTransferModule -- Cross() → CrossAsync() → CrossAgentToNewRegionAsync()
- Cross() fires async via WorkManager.RunInThreadPool.
- CrossAsync():
- Projects pos + velocity * 0.2s.
- Calls GetDestination() -- GridService.GetRegionByPosition() for the projected world position.
- Calls SimulationService.QueryAccess() -- checks destination will accept avatar.
- Removes avatar from physics: sp.RemoveFromPhysicalScene().
- Calls CrossAgentIntoNewRegionMain().
3. Agent State Sent to Destination
[edit]EntityTransferModule -- CrossAgentIntoNewRegionMain()
- Builds AgentData via sp.CopyTo(cAgent, isCrossUpdate=true):
- Sets CrossingFlags |= 1.
- Includes CrossExtraFlags for mouse button state.
- Attachments copied with script states.
- Calls SimulationService.UpdateAgent() -- HTTP POST to destination.
- Note: for crossing, CreateAgent() is NOT called first if destination already has a child agent. UpdateAgent() is called directly.
- Destination IncomingUpdateChildAgent() fires, sp.UpdateChildAgent() → CopyFrom() sets m_gotCrossUpdate=true.
- Sets sp.IsChildAgent = true.
- Sends CrossRegion event queue message to viewer -- gives destination handle, position, velocity, endpoint, caps path.
- Calls sp.HasMovedAway() and sp.MakeChildAgent().
- ResetFromTransit() called -- clears transit state.
If UpdateAgent fails (region refused):
- Calls ReInstantiateScripts() -- restores attachment scripts from InTransitScriptStates.
- Calls sp.AddToPhysicalScene() -- puts avatar back in physics.
- Returns false -- CheckForBorderCrossing() calls sp.CrossToNewRegionFail() which repositions avatar inside the region.
4. Avatar Becomes Root at Destination
[edit]Destination side CompleteMovement() fires when viewer sends CompleteMovementToRegion:
- m_gotCrossUpdate = true (set by CopyFrom).
- WaitForUpdateAgent() skipped for crossing (not teleport -- no SenderWantsToWaitForRoot set for crossing path).
- MakeRootAgent() called. Physics created. Position from AgentData.
- SendRegionHandshake() skipped (not a crossing -- m_gotCrossUpdate suppresses it).
- MoveAgentIntoRegion() sends viewer its new position.
- Attachments: scripts restarted via RestartAttachmentScripts() (not RezAttachments).
- EnableChildAgents() seeds new neighbours.
Summary: DB Tables Touched
[edit]| Table | Operation | Step | Notes |
|---|---|---|---|
| regions | SELECT | Destination lookup (step 2) | ROBUST GridService |
| griduser | UPDATE | Post-arrival at destination (step 9) | LastRegionID, LastPosition |
| presence | UPDATE | Post-arrival at destination (step 10) | RegionID |
No asset or inventory writes occur during a normal teleport or crossing. Attachments are carried in AgentData in memory; they are only written to assets/inventory on detach or logout.
Key Differences: Teleport vs Crossing
[edit]| Aspect | Teleport | Crossing |
|---|---|---|
| Initiator | Viewer request | Physics / heartbeat frame |
| Protocol | CreateAgent then UpdateAgent | UpdateAgent only (if child exists) |
| Viewer notification | TeleportFinish event queue | CrossRegion event queue |
| Script state | Snapshotted in CopyTo, restored via RestartAttachmentScripts | Same |
| isCrossUpdate flag | false | true |
| WaitForUpdateAgent | Yes (up to 10s) | No |
| m_gotCrossUpdate | false | true |
| RegionHandshake sent | Yes (if not crossing) | No |
| Source closes | If OutSideViewRange, after 15s | Immediately (MakeChildAgent) |
Failure Modes
[edit]Teleport failure after CreateAgent (destination created but viewer can't connect):
- UpdateAgent() times out after ~10s.
- Fail() called: CleanupFailedInterRegionTeleport() -- SimulationService.CloseAgent() to destination, sp.IsChildAgent=false, scripts re-instantiated.
- TeleportFailed sent to viewer.
Crossing failure (UpdateAgent to destination fails):
- ReInstantiateScripts() restores attachment scripts.
- sp.AddToPhysicalScene() restores physics.
- sp.CrossToNewRegionFail() repositions avatar inside region bounds.
See Also
[edit]- OpenSimulator Internals/Walkthroughs
- OpenSimulator Internals/Code Map/EntityTransferModule
- OpenSimulator Internals/Code Map/ScenePresence
- OpenSimulator Internals/Code Map/Scene
- OpenSimulator Internals/Connector Architecture/Simulation Connector
- OpenSimulator Internals/Walkthroughs/Avatar Rez In Region
- OpenSimulator Internals/Walkthroughs/Avatar Logs Out
- OpenSimulator Internals/Walkthroughs/Avatar Goes HG