Jump to content

OpenSimulator Internals/Walkthroughs/Avatar Transfer Between Regions

From Open Simulator Technical Help
Revision as of 13:09, 7 July 2026 by Jwbshaw (talk | contribs) (first)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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()
  1. Viewer sends TeleportLocationRequest packet (or map click, lure, landmark).
  2. Scene.RequestTeleportLocation() gets ScenePresence, checks not deleted/in transit, calls EntityTransferModule.Teleport().
  3. 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()
  1. Calls GridService.GetRegionByPosition() -- HTTP GET to ROBUST GridService.
    • ROBUST reads regions table, returns GridRegion for the destination.
  2. Calls GetFinalDestination() -- on base class returns same region (HG override resolves foreign address).
  3. 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()
  1. DNS resolution: finalDestination.ExternalEndPoint.
  2. 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.
  3. Checks sp.Appearance.CanTeleport(ctx.OutboundVersion) -- rejects if outfit incompatible with destination protocol version.
  4. If avatar sitting: calls sp.StandUp().
  5. Sets sp.IsInLocalTransit, sp.IsInTransit = true.
  6. Sends TeleportStart to viewer.

4. Create Agent at Destination (V2)

[edit]
EntityTransferModule -- TransferAgent_V2() → CreateAgent()
  1. Builds AgentCircuitData for the destination -- includes caps path, start position, appearance height.
  2. Determines OutSideViewRange: whether destination is outside current draw distance (needs new child agent vs reusing existing one).
  3. 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.
  4. If failed: sends TeleportFailed to viewer, clears transit state.
  5. Fires EventManager.TriggerTeleportStart on source.

5. Send TeleportFinish to Viewer

[edit]
EntityTransferModule -- TransferAgent_V2()
  1. Sets sp.IsChildAgent = true on source (before UpdateAgent, to prevent race with destination requesting a child close).
  2. Sends TeleportFinishEvent via IEventQueue -- gives viewer: destination handle, endpoint, caps seed URL, teleport flags.
  3. Viewer receives TeleportFinish and begins connecting to destination simulator.

6. Send Full Agent State to Destination

[edit]
EntityTransferModule -- TransferAgent_V2() → UpdateAgent()
ScenePresence -- CopyTo()
  1. 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.
  2. 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.
  3. UpdateAgent() blocks on source until destination returns.

7. Avatar Becomes Root at Destination

[edit]
ScenePresence -- CompleteMovement() (destination side)
  1. Viewer sends CompleteMovementToRegion to destination simulator.
  2. Destination CompleteMovement() fires (see OpenSimulator Internals/Walkthroughs/Avatar Rez In Region steps 3-5 for detail).
  3. 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.
  4. MakeRootAgent() called -- physics actor created, position adjusted.
  5. Avatar data sent to all presences at destination.
  6. 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)
  1. 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.
  2. Calls sp.MakeChildAgent(destinationHandle) -- source presence becomes child. Physics removed. OnMakeChildAgent fires.
  3. Closes child agents: sp.CloseChildAgents(childRegionsToClose) or sp.closeAllChildAgents() for logout=true.
  4. If NeedsClosing (OutSideViewRange): waits up to 15s for sp.IsInTransit to clear, then calls m_scene.CloseAgent() -- fully removes source presence.
  5. 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:

  1. GridUserService.SetLastPosition() -- updates griduser table with new RegionID, position, look.
DB writes
  • griduser -- UPDATE: LastRegionID, LastPosition, LastLookAt

10. PresenceService Updated

[edit]
  1. 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())
  1. Checks not IsChildAgent, not IsInTransit, not sitting, PhysicsActor != null.
  2. Projects position: pos + velocity * FrameTime.
  3. If projected position is outside region bounds: calls Scene.CrossAgentToNewRegion().
  4. Scene.CrossAgentToNewRegion() checks AllowAvatarCrossing flag, then calls EntityTransferModule.Cross().

2. Destination Found

[edit]
EntityTransferModule -- Cross() → CrossAsync() → CrossAgentToNewRegionAsync()
  1. Cross() fires async via WorkManager.RunInThreadPool.
  2. 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().
  3. Calls CrossAgentIntoNewRegionMain().

3. Agent State Sent to Destination

[edit]
EntityTransferModule -- CrossAgentIntoNewRegionMain()
  1. Builds AgentData via sp.CopyTo(cAgent, isCrossUpdate=true):
    • Sets CrossingFlags |= 1.
    • Includes CrossExtraFlags for mouse button state.
    • Attachments copied with script states.
  2. 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.
  3. Sets sp.IsChildAgent = true.
  4. Sends CrossRegion event queue message to viewer -- gives destination handle, position, velocity, endpoint, caps path.
  5. Calls sp.HasMovedAway() and sp.MakeChildAgent().
  6. ResetFromTransit() called -- clears transit state.

If UpdateAgent fails (region refused):

  1. Calls ReInstantiateScripts() -- restores attachment scripts from InTransitScriptStates.
  2. Calls sp.AddToPhysicalScene() -- puts avatar back in physics.
  3. 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:

  1. m_gotCrossUpdate = true (set by CopyFrom).
  2. WaitForUpdateAgent() skipped for crossing (not teleport -- no SenderWantsToWaitForRoot set for crossing path).
  3. MakeRootAgent() called. Physics created. Position from AgentData.
  4. SendRegionHandshake() skipped (not a crossing -- m_gotCrossUpdate suppresses it).
  5. MoveAgentIntoRegion() sends viewer its new position.
  6. Attachments: scripts restarted via RestartAttachmentScripts() (not RezAttachments).
  7. 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]