Jump to content

OpenSimulator Internals/Code Map/ScenePresence

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

OpenSimulator Internals/Code Map/ScenePresence

[edit]

Overview

[edit]
OpenSim/Region/Framework/Scenes/ScenePresence.cs

One ScenePresence instance exists per agent per region. It represents the avatar -- whether root (avatar is here) or child (avatar is elsewhere, this is a listening post for interest management). ScenePresence implements IScenePresence and EntityBase.


Lifecycle States

[edit]

Governed by ScenePresenceStateMachine. The critical flag for most code paths is IsChildAgent (bool).

State IsChildAgent Description
Child true Pre-seeded in neighbouring region. Tracks position updates, no physics, no full scene state.
Root false Avatar is present in this region. Full physics, full scene state, heartbeat participation.
Deleted -- IsDeleted set. Dispose() called.

Transition from child to root happens in MakeRootAgent(), called from CompleteMovement(). Transition from root to child happens in MakeChildAgent(), called by EntityTransferModule on departure.

See OpenSimulator Internals/Walkthroughs/Avatar Rez In Region for the full transition sequence on login.


Constructor

[edit]
ScenePresence(IClientAPI client, Scene world, AvatarAppearance appearance, PresenceType type)
  1. Sets IsChildAgent = true.
  2. Sets draw distance from client.StartFar, clamped to region MaxDrawDistance / DefaultDrawDistance.
  3. Sets RegionHandle, ControllingClient, name fields, UUID, LocalId.
  4. Fetches UserAccount -- sets m_userFlags and GodController god level. If no account found, god level 0.
  5. Initialises ScenePresenceAnimator and MovementAnimationOverrides.
  6. Sets AbsolutePosition from ControllingClient.StartPos.
  7. Disables child updates and reprioritization (m_childUpdatesBusy = true, m_reprioritizationBusy = true) -- re-enabled after CompleteMovement.
  8. Calls AdjustKnownSeeds() -- initialises m_knownChildRegions from CapsModule.
  9. Calls RegisterToClientEvents() -- see Event Wiring below.
  10. Sets Appearance from passed AvatarAppearance.
  11. Constructs ScenePresenceStateMachine.
  12. Calls ControllingClient.RefreshGroupMembership().

Event Wiring

[edit]

RegisterToClientEvents() hooks the following on ControllingClient:

Event Handler
OnCompleteMovementToRegion CompleteMovement()
OnAgentUpdate HandleAgentUpdate()
OnAgentCameraUpdate HandleAgentCamerasUpdate()
OnAgentRequestSit HandleAgentRequestSit()
OnAgentSit HandleAgentSit()
OnSetAlwaysRun HandleSetAlwaysRun()
OnStartAnim HandleStartAnim()
OnStopAnim HandleStopAnim()
OnChangeAnim avnHandleChangeAnim()
OnForceReleaseControls HandleForceReleaseControls()
OnAutoPilotGo MoveToTargetHandle()
OnRegionHandShakeReply RegionHandShakeReply()

All removed in RemoveClientEvents() on disposal.


MakeRootAgent()

[edit]
private bool MakeRootAgent(Vector3 pos, bool isFlying, ref Vector3 lookat)

Called only from CompleteMovement(). Returns false if already root.

  1. Acquires m_completeMovementLock.
  2. Handles pending sit (ParentUUID set): resolves prim, re-seats avatar.
  3. Sets IsChildAgent = false.
  4. Updates RegionHandle.
  5. Fires EventManager.TriggerSetRootAgentScene().
  6. If not sitting:
    • Calls CheckAndAdjustLandingPoint_OS() or _SL() -- adjusts position for telehubs, parcel landing points, ban lines.
    • Clamps position to region bounds.
    • Raises Z to ground height + avatar half-height.
    • If ViaLogin or ViaLocation flags: raycasts downward from PhysSearchHeight (300f) using RayFilterFlags.PrimsNonPhantomAgents to find surface, avoids placing avatar inside objects.
    • Sets AbsolutePosition.
    • Calls AddToPhysicalScene(isFlying).
  7. Calls m_scene.SwapRootAgentCount(false, IsNPC).
  8. Resets MovementFlags = 0.
  9. Updates circuit: AuthenticateHandler.UpdateAgentChildStatus(circuitCode, false).
  10. Fires EventManager.TriggerOnMakeRootAgent(this).

Physics position search constants:

  • PhysSearchHeight = 300f
  • PhysMinSkipGap = 20f
  • PhysSkipGapDelta = 30f
  • PhysNumberCollisions = 30

MakeChildAgent()

[edit]
public void MakeChildAgent(ulong newRegionHandle)

Called by EntityTransferModule when avatar departs.

  1. Resets m_updateAgentReceivedAfterTransferEvent.
  2. Clears group information and cross-update flags.
  3. Unhooks RegionHeartbeatEnd.
  4. Sets IsChildAgent = true.
  5. Calls m_scene.SwapRootAgentCount(true, IsNPC).
  6. Calls RemoveFromPhysicalScene().
  7. Clears parcel tracking.
  8. Sends kill packets to root presences that don't know the new region (won't receive the avatar as child).
  9. Updates circuit: AuthenticateHandler.UpdateAgentChildStatus(circuitCode, true).
  10. Fires EventManager.TriggerOnMakeChildAgent(this).

CompleteMovement()

[edit]
public void CompleteMovement(IClientAPI client, bool openChildAgents)

Triggered by OnCompleteMovementToRegion event from viewer.

  • For teleports/crossings: calls WaitForUpdateAgent() -- waits up to 10 seconds for source region's UpdateAgent to arrive (sets m_originRegionID via CopyFrom()).
  • For real login (IsRealLogin() true): skips WaitForUpdateAgent().
  • Calls MakeRootAgent().
  • Sends RegionHandshake if not a crossing.
  • Calls ControllingClient.MoveAgentIntoRegion().
  • Validates baked texture cache (AvatarFactory.ValidateBakedTextureCache()); queues appearance save if invalid.
  • Sends initial avatar data to all presences.
  • Sends appearance and animations to self and others.
  • For real login: calls Scene.AttachmentsModule.RezAttachments(this).
  • For crossing with carried attachments: restarts scripts, sends attachment updates.
  • For login: calls IFriendsModule.SendFriendsOnlineIfNeeded().
  • Hooks RegionHeartbeatEnd for per-frame processing.

IsRealLogin() check:

(teleportFlags & (ViaLogin | ViaHGLogin)) == ViaLogin

RegionHandShakeReply() / SendInitialData()

[edit]
public void RegionHandShakeReply(IClientAPI client)
private void SendInitialData()

RegionHandShakeReply() sets NeedInitialData = 2 on receipt of viewer handshake reply.

SendInitialData() is called from Update() when NeedInitialData > 0. It waits for:

  • ViewerFlags.SentSeeds to be set (caps seeds sent to viewer).
  • NeedInitialData incremented to 6 (several heartbeat frames for viewer to process seeds).

Then fires async (Util.FireAndForget):

  • Releases source region via SimulationService.ReleaseAgent() if callback URI present.
  • Calls IEntityTransferModule.CloseOldChildAgents().
  • Sends terrain layer data if not a teleport.
  • Sends parcel info.
  • Sends full avatar updates for all other root presences (SendOtherAgentsAvatarFullToMe()).
  • Sends ObjectUpdate packets for all scene objects -- full updates or cache probes depending on ViewerFlags.
  • Calls IEntityTransferModule.EnableChildAgents() -- seeds neighbouring regions.
  • Sets m_childUpdatesBusy = false.

Update()

[edit]

Called each heartbeat frame from Scene.Update() for every ScenePresence.

  • If NeedInitialData > 0: calls SendInitialData() and returns.
  • If IsChildAgent or IsInTransit: returns.
  • Calls CheckForBorderCrossing().
  • Handles MoveToTarget autopilot updates.
  • Handles delayed stop.
  • If avatar size changed: sends avatar data to all agents.
  • If position, velocity, rotation, or collision plane changed beyond tolerance: calls SendTerseUpdateToAllClients().
  • Calls CheckForSignificantMovement().

Physical Scene

[edit]

AddToPhysicalScene(bool isFlying):

  • Calls PhysicsScene.AddAvatar() -- creates PhysicsActor.
  • Wires pa.OnCollisionUpdate → PhysicsCollisionUpdate().
  • Wires pa.OnOutOfBounds → OutOfBoundsCall().
  • Sets PhysicsActor = pa.

RemoveFromPhysicalScene():

  • Interlocked.Exchange to null (thread-safe).
  • Unwires collision and OOB events.
  • Calls PhysicsScene.RemoveAvatar().

Neighbour Region Tracking

[edit]

m_knownChildRegions: Dictionary<ulong, string> -- regionHandle → seed cap URL. m_knownChildRegionsSizeInfo: Dictionary<ulong, spRegionSizeInfo> -- regionHandle → (sizeX, sizeY).

Key methods:

  • AddNeighbourRegion() / RemoveNeighbourRegion()
  • GetChildAgentsToClose() -- computes which neighbours to close on departure, based on IsOutsideView() distance check (currently hardcoded 255 rather than RegionViewDistance).
  • CloseChildAgents() -- sends CloseChildAgentConnections to computed list, drops cap seeds.

Sitting

[edit]

HandleAgentRequestSit() → SendSitResponse() → HandleAgentSit() / PhysicsSit().

PhysicsSit() delegates to PhysicsScene.SitAvatar() with callback PhysicsSitResponse(). Falls back to legacy sit if physics engine doesn't support it.

On sit:

  • RemoveFromPhysicalScene().
  • ParentID = part.LocalId, ParentPart = part.
  • part.AddSittingAvatar(this).
  • Animator.SetMovementAnimations("SIT").

StandUp():

  • Computes world position from sit offset and prim rotation.
  • Clears ParentID, ParentPart.
  • Calls AddToPhysicalScene() if addPhys true.
  • part.RemoveSittingAvatar(this).
  • Animator.SetMovementAnimations("STAND").

SIT_TARGET_ADJUSTMENT = (0, 0, 0.4f) -- empirically derived fudge factor matching SL behaviour (Mantis 1716).


Parcel Visibility

[edit]

ParcelHideThisAvatar: true if current parcel has SeeAVs = false.

On parcel crossing (currentParcelUUID setter → ParcelCrossCheck()):

  • Computes which presences need kill packets (now hidden from them or they from us).
  • Computes which presences need view packets (now visible).
  • Sends kills and views accordingly.

Child Agent Data Transfer

[edit]

CopyTo(AgentData, isCrossUpdate): serialises full avatar state for transfer to destination region -- position, velocity, camera, appearance, animations, attachments, script controls, group info, friends online cache.

CopyFrom(AgentData): deserialises on destination side. Sets m_originRegionID, which signals WaitForUpdateAgent() to unblock CompleteMovement().

UpdateChildAgent(AgentData): called on destination when source fires UpdateAgent. Calls CopyFrom() then sets m_updateAgentReceivedAfterTransferEvent.

UpdateChildAgent(AgentPosition): lightweight position-only update for child agent interest management. Adjusts position by region offset, updates throttles with distance-based scaling factor.


Movement

[edit]

HandleAgentUpdate(): main movement handler. Processes AgentUpdateArgs from viewer each packet.

  • Drops updates where IsChildAgent or IsInTransit.
  • Updates DrawDistance, HideTitle, MuteCollisions, mouse state, State byte.
  • Computes movement vector from control flags.
  • Calls AddNewMovement() → sets PhysicsActor.TargetVelocity.
  • Calls Animator.UpdateMovementAnimations().
  • Calls SendControlsToScripts() -- fires EventManager.TriggerControlEvent for scripted control listeners.

Autopilot: MoveToTarget() / HandleMoveToTargetUpdate() -- PID-style movement toward a target position, used by llMoveToTarget() and parcel enforcement.

Border crossing detection: CheckForBorderCrossing() -- projects position by velocity * FrameTime, if outside region bounds calls CrossToNewRegion() → Scene.CrossAgentToNewRegion().


Attachment Management

[edit]

m_attachments: List<SceneObjectGroup>, locked on AttachmentsSyncLock.

AddAttachment() / RemoveAttachment() / GetAttachments() / ClearAttachments().

SendAttachmentsToAgentNF(): sends terse update then full updates for all attachments to a specific presence. SendAttachmentScheduleUpdate(): sends pending flagged updates to self and all visible presences.


Animation

[edit]

Animator: ScenePresenceAnimator -- manages animation state machine. Overrides: MovementAnimationOverrides -- server-side animation override pairs.

HandleStartAnim() / HandleStopAnim() / avnHandleChangeAnim(): viewer-initiated animation control. RegionHeartbeatEnd hook: calls Animator.UpdateMovementAnimations() every 2 frames when root and not sitting.


Significant Movement

[edit]

CheckForSignificantMovement():

  • MOVEMENT (0.25f sq) threshold: fires EventManager.TriggerOnClientMovement().
  • SIGNIFICANT_MOVEMENT (16.0f sq) threshold: fires EventManager.TriggerSignificantClientMovement().
  • CHILDAGENTSCHECK_MOVEMENT (1024f sq / 32m) threshold: calls IEntityTransferModule.EnableChildAgents() to update neighbour seeds.
  • CHILDUPDATES_MOVEMENT (100f sq): sends AgentPosition updates to known child regions.

Key Constants

[edit]
Constant Value Purpose
MOVEMENT 0.25f Minimum movement to trigger client movement event (sq distance)
SIGNIFICANT_MOVEMENT 16.0f Minimum movement to trigger significant movement event (sq distance)
CHILDUPDATES_MOVEMENT 100.0f Minimum movement to send child agent position update (sq distance)
CHILDAGENTSCHECK_MOVEMENT 1024.0f Minimum movement to recheck neighbour child agents (sq distance, ~32m)
CHILDUPDATES_TIME 2000f Minimum ms between child agent updates
SIT_TARGET_ADJUSTMENT (0,0,0.4f) Sit target fudge factor matching SL (Mantis 1716)
PhysSearchHeight 300f Ray cast start height for spawn position search
PhysMinSkipGap 20f Minimum gap between surfaces in spawn position search
AgentControlNormalVel 4.096f Normal walk speed multiplier
AgentControlMidVel 0.6 * 4.096f Slow walk speed multiplier

Known Issues

[edit]
  • IncomingAttechments() typo in Scene.cs line 2976 and AttachmentsModule.cs line 384 (Mantis pending).
  • GetChildAgentsToClose() uses hardcoded 255 for view distance rather than RegionViewDistance -- comment in source acknowledges this.

See Also

[edit]