Jump to content

OpenSimulator Internals/Code Map/LLClientView

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

OpenSimulator Internals/Code Map/LLClientView

[edit]

Overview

[edit]

LLClientView is the concrete implementation of IClientAPI for the Second Life UDP protocol. It sits above LLUDPServer/LLUDPClient and translates parsed LLUDP packets into calls on the many IClientAPI events that Scene and its modules subscribe to, and conversely serializes outgoing scene state into LLUDP packets.

Source: OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs

Class declaration:

public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientInventory, IStatsCollector, IClientIPEndpoint

This is the largest single class in the codebase by source volume. It implements roughly 200 IClientAPI events and a dispatch table of over 200 packet handlers.


Construction

[edit]

Constructor takes: Scene, LLUDPServer, LLUDPClient, AuthenticateResponse sessionInfo, agentId, sessionId, circuitCode.

  1. Registers itself as IClientIM, IClientInventory, IClientChat via RegisterInterface (IClientCore pattern)
  2. Creates PriorityQueue for entity updates and entity property updates, sized to min(512, scene entity count)
  3. Looks up IAssetService and IGroupsModule from the scene
  4. Creates LLImageManager for texture streaming
  5. Copies agent identity from sessionInfo (firstname, lastname, session/secure session, start position)
  6. Hooks LLUDPClient events: OnQueueEmpty, HasUpdates, OnPacketStats
  7. Creates Prioritizer for update priority calculation
  8. Calls RegisterLocalPacketHandlers() -- registers the "autopilot" generic message handler
  9. Creates a JobEngine for async packet processing (5000ms timeout)

Start() calls m_asyncPacketProcess.Start() then Scene.AddNewAgent(this, PresenceType.User) -- this is what actually creates the ScenePresence.


Packet Dispatch

[edit]

Two dispatch tables, both keyed by PacketType:

Table Notes
m_staticHandlers FrozenDictionary<PacketType, StaticPacketProcessor> -- built once at class load via a static initializer covering ~230 packet types. Static methods take (LLClientView, Packet) so no per-instance delegate allocation.
m_packetHandlers Instance Dictionary<PacketType, PacketProcessor> -- populated by AddLocalPacketHandler(), used by region modules to add handlers without modifying this file

ProcessPacketMethod() checks m_staticHandlers first, then m_packetHandlers. Each entry carries an Async flag -- if true, the handler runs on the JobEngine (m_asyncPacketProcess) rather than the shared IncomingPacketHandler thread from LLUDPServer, so a slow handler for one client doesn't block packet processing for others.

ProcessInPacket() is the actual entry point called by LLUDPServer.IncomingPacketHandler() (via IncomingPacket.Client.ProcessInPacket()). Falls through to a warning log if no handler is registered for a packet type.


Outbound: Entity Updates

[edit]

This is the most performance-critical part of the class -- converting scene object and avatar state into the LLUDP wire format every frame.

Update Queueing

[edit]

SendEntityUpdate(entity, flags) computes a priority via Prioritizer.GetUpdatePriority() and enqueues an EntityUpdate into m_entityUpdates (a PriorityQueue). Kill flags and HUD-to-non-owner filtering happen here before queueing.

ProcessEntityUpdates

[edit]

Called from HandleQueueEmpty() (LLUDPClient throttle callback) with a byte budget. Dequeues from m_entityUpdates until the budget is exhausted or the queue is empty, sorting each update into one of several packet-type buckets:

Bucket Trigger Notes
terseUpdates Update only touches position/rotation/velocity/attachment/collision plane/textures (canNotUseImprovedMask check) Cheapest wire format -- ImprovedTerseObjectUpdate
objectUpdates ScenePresence full updates, or object full updates when not viewer-cachable ObjectUpdate packet, CreatePrimUpdateBlock/CreateAvatarUpdateBlock
compressedUpdates Object full updates when viewer object caching is supported and object IsViewerCachable Smaller wire format for objects the viewer may already have cached
objectUpdateProbes PrimUpdateFlags.UpdateProbe set ObjectUpdateCached packet -- asks viewer "do you already have this by CRC" before sending a full update
ObjectAnimationUpdates PrimUpdateFlags.Animations set and viewer supports object animations Separate ObjectAnimation packet per part

Per-object filtering before bucketing: skip if grp.inTransit, grp.IsDeleted (send kill instead), attachment owned by someone else's HUD, attachment owner not found or still child agent, attachment not actually in the owner's attachment list, distance culling (ObjectsCullingByDistance) against DrawDistance + ReprioritizationDistance + 16m.

All buckets pack into UDPPacketBuffer using LLUDPZeroEncoder inline (avoids intermediate byte[] allocation), splitting into additional packets when MAXPAYLOAD is approached mid-loop.

Distance Culling (CheckGroupsInView)

[edit]

When ObjectsCullingByDistance is enabled, this recomputes which SceneObjectGroups are within cullingrange (DrawDistance + ReprioritizationDistance + 16m) of the avatar every reprioritization cycle. Groups that fall out of range get a Kill sent; groups newly in range get a full update queued (with UpdateProbe if viewer caching is supported and the handshake flag for it hasn't been sent yet).


Region Handshake

[edit]

SendRegionHandshake() builds the RegionHandshake packet by hand using LLUDPZeroEncoder rather than the generic Packet classes, for performance. Contents: region flags (GetRegionFlags(), a large bitwise translation of RegionSettings/EstateSettings into the wire RegionFlags enum), sim access level, region name, estate owner, water height, billable factor, cache ID, four terrain texture or PBR texture IDs (SupportTerrainPBR flag chooses which), elevation blend values per corner, region ID, CPU class/ratio (hardcoded 9/1), product name, and extended region flags (64-bit) plus a RegionProtocols bitfield (bit 0 = server-side baking supported, bit 63 = more than 6 baked textures supported).

MoveAgentIntoRegion() sends AgentMovementComplete -- also hand-packed -- confirming the avatar's position, look direction, region handle, and simulator version string after CompleteAgentMovement is processed.


Connection Lifecycle

[edit]
Method Notes
Close(sendStop, force) Locked by CloseSyncLock to prevent races with relogin. Sends DisableSimulator if sendStop. Fires OnConnectionClosed. Stops async job engine. Flushes UDP server. Calls Scene.RemoveClient(). Unhooks UDP client events. Closes ImageManager, entity queues. Forces GC compaction if this was the last client in the scene.
Kick(message) Sends KickUser packet to root agents only, sleeps 500ms to let it land before the connection is torn down

Handling UseCircuitCode

[edit]

Note: the actual UseCircuitCode packet is handled in LLUDPServer.HandleUseCircuitCode() (see OpenSimulator Internals/Code Map/LLUDP), not here. The HandleUseCircuitCode static method inside LLClientView's own dispatch table is a no-op (entire body commented out) -- retained only so the packet type does not fall through to the "unhandled packet" warning.


Key Handler Categories

[edit]

The packet handler methods (all static, taking (LLClientView c, Packet packet)) are organized into named regions in source. Notable groups:

Region Representative packets Notes
Scene/Avatar AgentUpdate, SetAlwaysRun, AgentAnimation, AgentFOV, AgentThrottle, AgentPause/Resume HandleAgentUpdate implements significance thresholds (QDELTABody/Head, VDELTA) so trivial camera/body jitter below a cosine threshold does not trigger a full event dispatch -- see CheckAgentMovementUpdateSignificance / CheckAgentCameraUpdateSignificance
Objects/m_sceneObjects ObjectLink, ObjectDelink, ObjectAdd, ObjectShape, ObjectDuplicate, ObjectGrab/GrabUpdate/DeGrab, ObjectSpinStart/Update/Stop, MultipleObjectUpdate HandleMultipleObjUpdate decodes a compact bitfield (position/rotation/scale, individual vs group, uniform-scale) into ObjectChangeType values for onClientChangeObject
Inventory/Asset CreateInventoryFolder/Item, FetchInventoryDescendents, UpdateInventoryItem, RezScript, TransferRequest HandleTransferRequest spawns a background permission check (HandleSimInventoryTransferRequestWithPermsCheck) for SimInventoryItem source type before calling MakeAssetRequest
Parcel ParcelPropertiesRequest, ParcelDivide/Join, ParcelAccessListUpdate, ParcelReturnObjects, LandStatRequest
Estate EstateOwnerMessage (itself a sub-dispatch on a string method name: getinfo, setregioninfo, texturedetail, restart, estateaccessdelta, terrain, telehub, refreshmapvisibility, etc.), EstateCovenantRequest
Groups CreateGroupRequest, GroupRoleDataRequest, GroupMembersRequest, JoinGroupRequest, InviteGroupRequest Delegates to IGroupsModule; returns early if module is not loaded
God RequestGodlikePowers, GodKickUser, GodUpdateRegionInfo
Money/Economy MoneyTransferRequest, MoneyBalanceRequest, ObjectBuy, ParcelBuy

Notable Implementation Details

[edit]
  • AgentUpdate packets arrive roughly 10x/second per client even when idle. HandleAgentUpdate filters for significance (movement flags, body rotation beyond a near-1.0 cosine threshold, camera position/axis beyond a 0.01 delta) before invoking OnAgentUpdate/OnAgentCameraUpdate, and forces a full update at least every 500ms regardless.
  • Sequence-number gating: HandleAgentUpdate drops any AgentUpdate whose sequence number is not strictly greater than the last one processed, to avoid reordered/duplicate UDP packets corrupting movement state.
  • ObjectImage (texture updates) tracks a per-prim sequence number in objImageSeqs to discard stale/duplicate texture-set requests, expiring the tracking dictionary every 30 seconds.
  • DeRezObject supports multi-packet reassembly (m_DeRezObjectDelayed) when a single de-rez request is split across packets by the viewer.
  • CreateCompressedUpdateBlockZC has a large commented-out non-zero-encoded twin (CreateCompressedUpdateBlock) retained in source as a reference/fallback that is not currently compiled in.
  • GetViewerCaps() reads capability flags from the CAPS module (SentSeeds, ObjectAnim, WLEnv/AdvEnv for windlight environment support, PBR, TPBR for terrain PBR) and caches which optional features (m_SupportObjectAnimations, m_SupportPBR, SupportTerrainPBR) this specific viewer connection supports, gating several of the update-encoding code paths above.
  • SendEstateList, SendBannedUserList and related estate list sends batch at 63 entries per EstateOwnerMessage packet to stay under datagram size.

See Also

[edit]