Table of Contents

Class ObserverStreamingRegistry

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Server-side scheduler that applies ObserverStreamingPolicy to every registered character: scales each one's observer range by local density, and for every viewing client ranks what it can see and rate limits everything beyond the cap.

public static class ObserverStreamingRegistry
Inheritance
ObserverStreamingRegistry
Inherited Members

Remarks

Characters register from CharacterPredictionController.OnStartServer — players, monsters, pets, and the service NPCs built on the same prefab stack — because their NetworkTransform and other per-tick traffic is what an observer's bandwidth is spent on. Static classified objects (world items, waypoints) have no such hook and are adopted by ObserverBudgetCondition on first evaluation, so that every classification can be budgeted; they cost a position read per pass and nothing on the wire.

Runs one pass every RescheduleIntervalTicks ticks off FishNet.Managing.Timing.TimeManager.OnPostTick. A pass is O(entries) for density (uniform grid) plus O(viewers × entries) for ranking, with entries grouped by Unity scene so characters in different scenes never compare positions. At 200 players and 700 characters that is ~140k score evaluations per half second, all field reads.

Properties

Count

Number of objects currently registered — characters and budgeted statics alike.

public static int Count { get; }

Property Value

int

LastPassBudgetExcluded

(viewer, observed) pairs excluded by the visibility budget in the last pass.

public static int LastPassBudgetExcluded { get; }

Property Value

int

LastPassLimitedPairs

(viewer, observed) pairs rate limited in the last pass.

public static int LastPassLimitedPairs { get; }

Property Value

int

LastPassPinned

Characters pinned into a visibility budget in the last pass.

public static int LastPassPinned { get; }

Property Value

int

LastPassRangeChanges

Characters whose observer range was changed in the last pass.

public static int LastPassRangeChanges { get; }

Property Value

int

LastPassViewers

Viewers ranked in the last pass.

public static int LastPassViewers { get; }

Property Value

int

Methods

Clear()

Drops every registration. For tests and server shutdown.

public static void Clear()

Remarks

Through Detach(ObserverStreamingEntry, int) rather than Unregister(NetworkObject), which takes the object and so cannot remove an entry whose object has already been destroyed — exactly the entries a shutdown is most likely to be holding.

Get(NetworkObject)

Returns the entry for a registered object, or null.

public static ObserverStreamingEntry Get(NetworkObject networkObject)

Parameters

networkObject NetworkObject

Returns

ObserverStreamingEntry

IsWithinVisibilityBudget(int, int, bool, out bool)

Whether a character is inside a viewer's visibility budget.

public static bool IsWithinVisibilityBudget(int viewerClientId, int observedObjectId, bool currentlyVisible, out bool hasRanking)

Parameters

viewerClientId int

Client id of the viewing connection.

observedObjectId int

Object id of the character being tested.

currentlyVisible bool

True when the viewer already observes it; widens the budget by the hysteresis.

hasRanking bool

False when this viewer has no ranking yet.

Returns

bool

True when the character is inside the budget.

Remarks

The budget condition's whole implementation. hasRanking distinguishes "ranked and excluded" from "no pass has run for this viewer yet" — the caller must not treat the second as a rejection, or everything stays hidden until the first pass lands.

LogSummary()

Logs a one-line summary of the last pass. For diagnostics.

public static void LogSummary()

Register(NetworkObject, ICharacter)

Registers a character. Installs the entry as the object's observer send filter.

public static ObserverStreamingEntry Register(NetworkObject networkObject, ICharacter character)

Parameters

networkObject NetworkObject
character ICharacter

Returns

ObserverStreamingEntry

RegisterObject(NetworkObject)

Registers a classified object that nothing else registered — a dropped item, a waypoint.

public static ObserverStreamingEntry RegisterObject(NetworkObject networkObject)

Parameters

networkObject NetworkObject

The object to track.

Returns

ObserverStreamingEntry

The entry, or null when the object is not classified.

Remarks

These have no CharacterPredictionController to register them from, so the call comes from ObserverBudgetCondition the first time the object is evaluated: that condition runs on the server for every object carrying the manager's defaults, and it already holds the NetworkObject. The first evaluation defers (the object is unranked, which is admitted) and the next scheduling pass ranks it — the same deferral a freshly connected viewer gets, and the reason neither shows as a pop-in.

Only objects that declare a ClassifiedDistanceCondition are taken. Anything else has no budget to be measured against, and registering it would cost a per-pass position read for a decision that would always be "admit".

RunPass()

Runs one scheduling pass over every registered character. Public so a server can force a pass after a bulk change (a load boundary) and so tests can drive it.

public static void RunPass()

TryGetNearestViewerDistance(NetworkObject, out float)

Distance from networkObject to the nearest player in its scene, as measured by the last scheduling pass.

public static bool TryGetNearestViewerDistance(NetworkObject networkObject, out float distance)

Parameters

networkObject NetworkObject

The object to measure from.

distance float

Distance to the nearest player, or PositiveInfinity when the scene holds none.

Returns

bool

True when a pass has measured this object.

Remarks

The proximity primitive server-side simulation should ask, in place of NetworkObject.Observers. Observer membership answers "who is this object being streamed to", which the visibility budget bounds — a monster outside every viewer's budget has no observers and is still standing in a crowd. This answers "how close is the nearest player", which is what a leash, a wake-up or an aggro sweep actually wants to know.

False when the object is not registered, or when no pass has measured it yet. Callers must not read that as "nobody is near": it is an unanswered question, and the answer arrives within one RescheduleIntervalTicks.

Unregister(NetworkObject)

Unregisters a character, restoring its prefab range and removing the send filter.

public static void Unregister(NetworkObject networkObject)

Parameters

networkObject NetworkObject