Table of Contents

Class CharacterAttributeController

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Controls all character attributes and resource attributes for an entity. Handles initialization from template databases, network payload serialization, parent/child/dependency relationship wiring, tick-based resource regeneration, and reconcile-driven synchronization of both base and resource attributes via the unified CharacterReconcileData. There is no longer a separate broadcast path for non-resource attributes.

public class CharacterAttributeController : CharacterBehaviour, ICharacterAttributeController, ICharacterBehaviour, IPredictableController
Inheritance
Object
Component
Behaviour
MonoBehaviour
NetworkBehaviour
CharacterAttributeController
Implements
Inherited Members
NetworkBehaviour.IsSpawned
NetworkBehaviour.ComponentIndex
NetworkBehaviour.NetworkObject
NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS
NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID
NetworkBehaviour.ToString()
NetworkBehaviour.Reset()
NetworkBehaviour.OnValidate()
NetworkBehaviour.IsBehaviourReconciling
NetworkBehaviour.ClearReplicateCache()
NetworkBehaviour.CreateReconcile()
NetworkBehaviour.Reconcile_Reader<T>(PooledReader, ref T)
NetworkBehaviour.OnStartServerCalled
NetworkBehaviour.OnStartClientCalled
NetworkBehaviour.OnStopNetwork()
NetworkBehaviour.OnStartServer()
NetworkBehaviour.OnStopServer()
NetworkBehaviour.OnOwnershipServer(NetworkConnection)
NetworkBehaviour.OnSpawnServer(NetworkConnection)
NetworkBehaviour.OnDespawnServer(NetworkConnection)
NetworkBehaviour.OnStartClient()
NetworkBehaviour.OnStopClient()
NetworkBehaviour.OnOwnershipClient(NetworkConnection)
NetworkBehaviour.ClearBuffedRpcs()
NetworkBehaviour.ExcludeOwnerFromUnbufferedObserversRpcs
NetworkBehaviour.IsClientOnly
NetworkBehaviour.IsServerOnly
NetworkBehaviour.IsHost
NetworkBehaviour.IsClient
NetworkBehaviour.IsServer
NetworkBehaviour.IsDeinitializing
NetworkBehaviour.NetworkManager
NetworkBehaviour.ServerManager
NetworkBehaviour.ClientManager
NetworkBehaviour.ObserverManager
NetworkBehaviour.TransportManager
NetworkBehaviour.TimeManager
NetworkBehaviour.SceneManager
NetworkBehaviour.PredictionManager
NetworkBehaviour.RollbackManager
NetworkBehaviour.NetworkObserver
NetworkBehaviour.IsClientInitialized
NetworkBehaviour.IsClientStarted
NetworkBehaviour.IsClientOnlyInitialized
NetworkBehaviour.IsClientOnlyStarted
NetworkBehaviour.IsServerInitialized
NetworkBehaviour.IsServerStarted
NetworkBehaviour.IsServerOnlyInitialized
NetworkBehaviour.IsServerOnlyStarted
NetworkBehaviour.IsHostInitialized
NetworkBehaviour.IsHostStarted
NetworkBehaviour.IsOffline
NetworkBehaviour.IsNetworked
NetworkBehaviour.GetIsNetworked()
NetworkBehaviour.IsManagerReconciling
NetworkBehaviour.Observers
NetworkBehaviour.IsOwner
NetworkBehaviour.IsController
NetworkBehaviour.HasAuthority
NetworkBehaviour.Owner
NetworkBehaviour.OwnerId
NetworkBehaviour.ObjectId
NetworkBehaviour.LocalConnection
NetworkBehaviour.OwnerMatches(NetworkConnection)
NetworkBehaviour.Despawn(GameObject, DespawnType?)
NetworkBehaviour.Despawn(NetworkObject, DespawnType?)
NetworkBehaviour.Despawn(DespawnType?)
NetworkBehaviour.Spawn(GameObject, NetworkConnection, Scene)
NetworkBehaviour.Spawn(NetworkObject, NetworkConnection, Scene)
NetworkBehaviour.RemoveOwnership()
NetworkBehaviour.GiveOwnership(NetworkConnection)
NetworkBehaviour.GetInstance<T>()
NetworkBehaviour.TryRegisterInstance<T>(T)
NetworkBehaviour.UnregisterInstance<T>()
NetworkBehaviour.CanLog(LoggingType)
MonoBehaviour.IsInvoking()
MonoBehaviour.CancelInvoke()
MonoBehaviour.StopCoroutine(Coroutine)
MonoBehaviour.StopAllCoroutines()
MonoBehaviour.destroyCancellationToken
MonoBehaviour.useGUILayout
MonoBehaviour.didStart
MonoBehaviour.didAwake
MonoBehaviour.runInEditMode
Behaviour.enabled
Behaviour.isActiveAndEnabled
Component.GetComponent<T>()
Component.TryGetComponent<T>(out T)
Component.GetComponentInChildren<T>()
Component.GetComponentsInChildren<T>()
Component.GetComponentInParent<T>()
Component.GetComponentsInParent<T>()
Component.GetComponents<T>()
Component.GetComponentIndex()
Component.CompareTag(TagHandle)
Component.transform
Component.transformHandle
Component.gameObject
Component.tag
Object.GetEntityId()
Object.GetInstanceID()
Object.GetHashCode()
Object.InstantiateAsync<T>(T)
Object.InstantiateAsync<T>(T, Transform)
Object.InstantiateAsync<T>(T, Vector3, Quaternion)
Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion, Transform)
Object.Instantiate(Object)
Object.Instantiate(Object, Scene)
Object.Instantiate<T>(T, InstantiateParameters)
Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)
Object.Instantiate(Object, Transform)
Object.Instantiate<T>(T)
Object.Instantiate<T>(T, Vector3, Quaternion)
Object.Instantiate<T>(T, Vector3, Quaternion, Transform)
Object.Instantiate<T>(T, Transform)
Object.Destroy(Object)
Object.DestroyImmediate(Object)
Object.DontDestroyOnLoad(Object)
Object.DestroyObject(Object)
Object.FindObjectsOfType<T>()
Object.FindObjectsByType<T>(FindObjectsSortMode)
Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)
Object.FindObjectOfType<T>()
Object.FindFirstObjectByType<T>()
Object.FindAnyObjectByType<T>()
Object.FindFirstObjectByType<T>(FindObjectsInactive)
Object.FindAnyObjectByType<T>(FindObjectsInactive)
Object.FindObjectsByType<T>()
Object.FindObjectsByType<T>(FindObjectsInactive)
Object.name
Object.hideFlags
Extension Methods

Fields

CharacterAttributeDatabase

Reference to the ScriptableObject database containing all character attribute templates. Used to initialize and manage available attributes for this character.

public CharacterAttributeTemplateDatabase CharacterAttributeDatabase

Field Value

CharacterAttributeTemplateDatabase

HealthRegenerationTemplateID

Template ID for the health regeneration attribute.

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int HealthRegenerationTemplateID

Field Value

int

HealthResourceTemplateID

Template ID for the health resource attribute (e.g., HP).

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int HealthResourceTemplateID

Field Value

int

ManaRegenerationTemplateID

Template ID for the mana regeneration attribute.

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int ManaRegenerationTemplateID

Field Value

int

ManaResourceTemplateID

Template ID for the mana resource attribute (e.g., MP).

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int ManaResourceTemplateID

Field Value

int

StaminaRegenerationTemplateID

Template ID for the stamina regeneration attribute.

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int StaminaRegenerationTemplateID

Field Value

int

StaminaResourceTemplateID

Template ID for the stamina resource attribute.

[TemplateReference(typeof(CharacterAttributeTemplate))]
public int StaminaResourceTemplateID

Field Value

int

Properties

Attributes

Public accessor for all non-resource character attributes.

public Dictionary<int, CharacterAttribute> Attributes { get; }

Property Value

Dictionary<int, CharacterAttribute>

IsPropagating

True while the attribute graph is propagating value changes. Notifications are deferred until propagation completes.

public bool IsPropagating { get; }

Property Value

bool

Order

Execution order in the unified prediction pipeline. Runs after buff reconcile/tick and before ability processing so regenerated resources are available for same-tick activation checks.

public int Order { get; }

Property Value

int

Remarks

Must stay above Order (85) and Order (93). This is what stops attribute modifiers compounding on every reconcile, and it is load-bearing rather than tidy. Reconcile(CharacterReconcileData, Channel) walks the controllers in this order. Buff and equipment restore their own ATTRIBUTED entries — Buff.Apply and ItemGenerator.ApplyAttributes both call CharacterAttribute.SetSource — while ApplyAttributeSnapshot(AttributeReconcileEntry[]) installs the server's TOTAL, with SetModifierDirect, as the residual between that total and whatever those sources currently sum to. Running last is what makes the residual right: it is computed against a ledger this tick's buff and equipment restores have already settled. Reorder this below either of them and the residual is measured against the PREVIOUS tick's sources, so every buff gained or item equipped on the reconciled tick lands on the sheet twice until the next reconcile happens to be measured after it. Pinned by PredictionAuditRegressionTests.AttributeReconcile_RunsAfterBuffAndEquipment.

ResourceAttributes

Public accessor for all resource character attributes.

public Dictionary<int, CharacterResourceAttribute> ResourceAttributes { get; }

Property Value

Dictionary<int, CharacterResourceAttribute>

Methods

AddAttribute(CharacterAttribute)

Adds a non-resource attribute instance to the controller if not already present.

public void AddAttribute(CharacterAttribute instance)

Parameters

instance CharacterAttribute

The attribute instance to add.

AddResourceAttribute(CharacterResourceAttribute)

Adds a resource attribute instance to the controller if not already present.

public void AddResourceAttribute(CharacterResourceAttribute instance)

Parameters

instance CharacterResourceAttribute

The resource attribute instance to add.

ApplyObservedAttributes(AttributeReconcileEntry[], bool)

Applies a set of authoritative attribute entries by template id.

public void ApplyObservedAttributes(AttributeReconcileEntry[] entries, bool isFullSet = false)

Parameters

entries AttributeReconcileEntry[]

Entries to apply. Null or empty is a no-op.

isFullSet bool

True when entries states the character's entire sheet.

Remarks

Both the base value and the external modifier are applied, which is what lets the receiver arrive at the same FinalValue the server holds. Attributes the message does not name are left alone: the sheet is fixed when the character spawns, so an omission means "unchanged", never "removed".

That is exactly why isFullSet is a diagnostic here rather than a mode. It travelled on the wire and no consumer read it, which made the sender's one real use of it silent: FishMMO.Shared.CharacterAttributeController.MaybePushObservedAttributes() sets it when the sheet's LENGTH changed, meaning the character was rebuilt — and a receiver that merges by id cannot honour that. It cannot drop the attributes the message leaves out, because absence has always meant "unchanged". Since nothing removes an attribute at runtime the case does not arise today, so the flag is checked and reported rather than acted on: if the assumption ever stops holding, this says so instead of the sheets quietly diverging.

ApplyObservedResourceState(float, int, float, int, float, int)

Applies a resource state snapshot to restore health, mana, stamina, and the regen schedule. Used by FishNet's Replicate/Reconcile prediction system.

Each resource is processed independently via ApplyIndividualResourceState(int, int, float) so entities that legitimately lack one of the standard resources (NPCs without mana, monsters without stamina) still have their present resources correctly reconciled. A template ID of 0 means the resource is not present on this entity and is silently skipped.

public void ApplyObservedResourceState(float health, int maxHealth, float mana, int maxMana, float stamina, int maxStamina)

Parameters

health float
maxHealth int
mana float
maxMana int
stamina float
maxStamina int

Remarks

The observer broadcast is whole-unit values only; it deliberately carries no regen tick. See the call site for why applying a zeroed schedule would be wrong.

ApplyResourceState(CharacterAttributeResourceState)

Applies a resource state (health, mana, stamina, regen delta) to the controller.

public void ApplyResourceState(CharacterAttributeResourceState resourceState)

Parameters

resourceState CharacterAttributeResourceState

Resource state to apply.

BeginNotificationSuppression()

Begins a scope that suppresses all attribute update notifications while still allowing deterministic attribute/resource state mutations to run. Used by prediction replay so UI, ECA, and passive listeners do not observe intermediate resimulation states.

public void BeginNotificationSuppression()

BeginPropagation()

Begins a propagation batch. While active, EnqueueNotification(CharacterAttribute) collects changed attributes instead of firing events immediately. Must be paired with EndPropagation().

public void BeginPropagation()

ClearModifierSource(ModifierSource)

Releases one contributor from every attribute on the sheet.

public void ClearModifierSource(ModifierSource source)

Parameters

source ModifierSource

The contributor to release everywhere. Only its Kind and Id are read.

Remarks

For a source whose owner goes away without knowing which attributes it touched — a region a character walks out of, which cannot enumerate what its authored actions modified. Cheap because ClearSource(ModifierSource) is a no-op on an attribute that source never touched, and an attribute holds a handful of contributors at most.

Resources are walked too: a region action is free to modify a resource's maximum, and a contributor the sheet forgot on one half would be exactly the orphaned modifier the ledger exists to prevent.

Every entry the contributor holds, not the one entry source names. A contributor may write to one attribute more than once — see Index — and this is the call for a source whose owner is going away, so releasing one index and stranding the rest would be exactly the orphaned modifier above. Index is therefore ignored here: the caller names the CONTRIBUTOR and gets all of it.

EndNotificationSuppression()

Ends a notification-suppression scope and discards any queued updates without invoking OnAttributeUpdated listeners.

public void EndNotificationSuppression()

EndPropagation()

Ends a propagation batch. If this is the outermost batch (depth returns to zero), fires all deferred OnAttributeUpdated events.

public void EndPropagation()

EnqueueNotification(CharacterAttribute)

Enqueues an attribute for deferred notification during propagation. If not propagating, callers should invoke the event directly instead.

public void EnqueueNotification(CharacterAttribute attribute)

Parameters

attribute CharacterAttribute

The attribute to notify after propagation completes.

GetResourceState()

Captures the current resource state as a snapshot for FishNet's Replicate/Reconcile prediction system. Each resource is sampled independently; a template ID of 0 means the resource is not present on this entity and its fields remain at the default (0), which is harmless for the delta serializer.

public CharacterAttributeResourceState GetResourceState()

Returns

CharacterAttributeResourceState

A snapshot of present resource values plus the regen schedule.

InitializeAttributeDependents()

Initializes parent/child/dependency relationships for all non-resource attributes.

public void InitializeAttributeDependents()

InitializeOnce()

CharacterBehaviour function which is typically called once during initialization and typically before Awake or OnAwake. Initializes all attributes and resource attributes from the CharacterAttributeDatabase, and sets up dependencies.

public override void InitializeOnce()

InitializeResourceAttributeDependents()

Initializes parent/child/dependency relationships for all resource attributes.

public void InitializeResourceAttributeDependents()

OnCreateReconcile(ref CharacterReconcileData)

Writes this controller's authoritative state into the reconcile data (server only). Called once per tick after OnReplicate(ref CharacterReplicateData, ReplicateState, Channel).

public void OnCreateReconcile(ref CharacterReconcileData reconcileData)

Parameters

reconcileData CharacterReconcileData

OnReconcile(CharacterReconcileData, Channel)

Restores resource state and the full non-resource attribute snapshot from authoritative reconcile data. Notifications are batched through the propagation system; dirty-tracking is suppressed during the restore so the rebuilt snapshot retains ReferenceEquals identity.

public void OnReconcile(CharacterReconcileData rd, Channel channel)

Parameters

rd CharacterReconcileData

Unified reconcile payload.

channel Channel

Transport channel.

Remarks

The owner always reconciles: this is the authority for the resource values its own predicted regeneration advances from.

A non-owner only reconciles a forwarded object. With forwarding off an observer's copy of this character is fed by CharacterResourcesBroadcast and CharacterAttributesBroadcast instead, and it would not receive this reconcile anyway — so the guard states the contract rather than defending against a message that cannot arrive. Stating it matters because the two paths carry the same fields at different precisions: the broadcast rounds resources to whole units and zeroes NextRegenTick, so letting both write would let a coarser value land on top of a finer one depending on arrival order. See ObserverSyncMode.

OnReplicate(ref CharacterReplicateData, ReplicateState, Channel)

Advances deterministic resource regeneration for the current prediction tick.

public void OnReplicate(ref CharacterReplicateData input, ReplicateState state, Channel channel)

Parameters

input CharacterReplicateData

Unified replicate input for this tick.

state ReplicateState

Current replicate execution state.

channel Channel

Transport channel.

OnStartNetwork()

Initializes the tick-based regen interval. Attribute state replication is handled entirely through the unified reconcile pipeline — there is no per-tick flush subscription.

public override void OnStartNetwork()

PopulateInput(ref CharacterReplicateData)

Attributes do not contribute owner input into CharacterReplicateData.

public void PopulateInput(ref CharacterReplicateData input)

Parameters

input CharacterReplicateData

Unified replicate input for this tick.

ReadPayload(NetworkConnection, Reader)

Reads attribute and resource attribute base values from the network payload and applies them.

public override void ReadPayload(NetworkConnection conn, Reader reader)

Parameters

conn NetworkConnection

The network connection.

reader Reader

The network reader containing serialized attribute data.

Regenerate(uint)

Processes resource regeneration for health, mana, and stamina using the authoritative simulation tick. Fires when the tick reaches the absolute FishMMO.Shared.CharacterAttributeController.nextRegenTick schedule point, then advances that point by FishMMO.Shared.CharacterAttributeController.regenTickInterval.

Absolute scheduling (vs modulo) prevents a runtime TickDelta change from immediately firing a spurious pulse: the existing scheduled tick fires naturally, and the new interval applies only for subsequent pulses.

IMPORTANT — Replay non-determinism: Regen is intentionally NOT replayed during rollback. The monotonic guard (FishMMO.Shared.CharacterAttributeController.lastProcessedRegenTick) causes all replayed ticks within the rollback window to be skipped. Authoritative resource values are restored by ApplyResourceState(CharacterAttributeResourceState) via reconcile. After a deep rewind, FishMMO.Shared.CharacterAttributeController.lastProcessedRegenTick can remain ahead of the replayed local tick until live simulation catches up, so client-side regen presentation may pause briefly after high-RTT reconciles. Server authority is unaffected because resource values continue to arrive through reconcile. Future maintainers must NOT assume replay-deterministic regen — gameplay systems that depend on intermediate regen events during replay will not observe them.

KNOWN OMISSION — the lockout state is predicted and NOT reconciled. FishMMO.Shared.CharacterAttributeController.lastProcessedRegenTick, FishMMO.Shared.CharacterAttributeController.lastConsumedResourceTicks and FishMMO.Shared.CharacterAttributeController.lastObservedResourceValues are all written from inside the replicate body and none of them travels in CharacterReconcileData. The owner and the server can therefore disagree about whether a resource is still inside the no-regeneration window that spending it opened, so a pulse can fire on one and not the other.

Left that way deliberately, and it is bounded: the resource VALUE is reconciled every tick, so a disagreement costs at most one pulse's worth of regeneration before the server's number overwrites it. Carrying three more fields — one of them a dictionary — on every reconcile to correct a sub-pulse timing difference is not a trade worth making. Anything that comes to depend on the lockout being exact must reconcile it first.

public void Regenerate(uint tick)

Parameters

tick uint

Authoritative simulation tick for this replicate step.

ResetState(bool)

Resets the state of all resource attributes by restoring their current values to their final values.

public override void ResetState(bool asServer)

Parameters

asServer bool

True if called on the server.

SetAttribute(int, int, int?)

Sets the base value and optional external modifier of a non-resource attribute by template ID.

public void SetAttribute(int id, int value, int? modifier = null)

Parameters

id int

The template ID of the attribute.

value int

The new base value.

modifier int?

Optional external modifier value to set.

SetResourceAttribute(int, int, float, int?)

Sets the base value, current value, and optional external modifier of a resource attribute by template ID.

public void SetResourceAttribute(int id, int value, float currentValue, int? modifier = null)

Parameters

id int

The template ID of the resource attribute.

value int

The new base value.

currentValue float

The new current depletable value.

modifier int?

Optional external modifier value to set.

TryGetAttribute(CharacterAttributeTemplate, out CharacterAttribute)

Attempts to retrieve a non-resource attribute by its template reference.

public bool TryGetAttribute(CharacterAttributeTemplate template, out CharacterAttribute attribute)

Parameters

template CharacterAttributeTemplate

The attribute template to look up.

attribute CharacterAttribute

The found attribute instance, or null.

Returns

bool

True if the attribute was found.

TryGetAttribute(int, out CharacterAttribute)

Attempts to retrieve a non-resource attribute by template ID.

public bool TryGetAttribute(int id, out CharacterAttribute attribute)

Parameters

id int

The template ID to look up.

attribute CharacterAttribute

The found attribute instance, or null.

Returns

bool

True if the attribute was found.

TryGetHealthAttribute(out CharacterResourceAttribute)

Attempts to retrieve the health resource attribute.

public bool TryGetHealthAttribute(out CharacterResourceAttribute health)

Parameters

health CharacterResourceAttribute

The found health resource attribute, or null.

Returns

bool

True if the health attribute was found.

TryGetManaAttribute(out CharacterResourceAttribute)

Attempts to retrieve the mana resource attribute.

public bool TryGetManaAttribute(out CharacterResourceAttribute mana)

Parameters

mana CharacterResourceAttribute

The found mana resource attribute, or null.

Returns

bool

True if the mana attribute was found.

TryGetResourceAttribute(CharacterAttributeTemplate, out CharacterResourceAttribute)

Attempts to retrieve a resource attribute by its template reference.

public bool TryGetResourceAttribute(CharacterAttributeTemplate template, out CharacterResourceAttribute attribute)

Parameters

template CharacterAttributeTemplate

The resource attribute template to look up.

attribute CharacterResourceAttribute

The found resource attribute instance, or null.

Returns

bool

True if the resource attribute was found.

TryGetResourceAttribute(int, out CharacterResourceAttribute)

Attempts to retrieve a resource attribute by template ID.

public bool TryGetResourceAttribute(int id, out CharacterResourceAttribute attribute)

Parameters

id int

The template ID to look up.

attribute CharacterResourceAttribute

The found resource attribute instance, or null.

Returns

bool

True if the resource attribute was found.

TryGetStaminaAttribute(out CharacterResourceAttribute)

Attempts to retrieve the stamina resource attribute.

public bool TryGetStaminaAttribute(out CharacterResourceAttribute stamina)

Parameters

stamina CharacterResourceAttribute

The found stamina resource attribute, or null.

Returns

bool

True if the stamina attribute was found.

WritePayload(NetworkConnection, Writer)

Writes all attribute and resource attribute base values to the network payload for client synchronization. Attributes are written in ascending Template.ID order for deterministic serialization across platforms regardless of Dictionary enumeration order.

public override void WritePayload(NetworkConnection conn, Writer writer)

Parameters

conn NetworkConnection

The network connection.

writer Writer

The network writer to serialize attribute data into.