Table of Contents

Class PredictedCombatEvents

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Tracks combat numbers a client has drawn from its OWN predicted hits, and reconciles them against the server's combat report.

public static class PredictedCombatEvents
Inheritance
PredictedCombatEvents
Inherited Members

Remarks

Why the client draws first. Damage numbers used to come only from CombatEventBroadcast, so a player's own hit showed nothing until the report came back — half a round trip of nothing happening, on the one action combat is built around. The caster predicted the cast and owns the input that produced it, so it can draw the number the moment its predicted projectile connects.

Why the amounts agree. Damage variance is drawn from the ability object's DeterministicRNG (RandomRangeValue reads EventData.RNG), whose state is carried in the reconcile and corrected every tick. Client and server draw the same number from the same state. A hit-count divergence advances the two generators differently until the next reconcile realigns them, so a mismatch is possible, bounded, and self-healing — which is why TryConfirm(ICharacter, ICharacter, Kind, int) matches on source, target and kind rather than on the amount.

Why there is no server-sent rejection. The server cannot tell a client "that hit did not land", because it never knew the client predicted one — it simply resolves its own simulation and reports what happened. Absence is the only signal available, so a prediction that goes unconfirmed for ConfirmationWindowSeconds is treated as rejected. That costs no bandwidth and needs no new message.

Pure bookkeeping with no Unity or rendering dependency, so the policy is unit tested. The client's display layer subscribes to the events below; nothing here draws anything.

Properties

ConfirmationWindowSeconds

How long a predicted number waits for the server to confirm it before being treated as rejected.

public static float ConfirmationWindowSeconds { get; set; }

Property Value

float

Remarks

Must comfortably exceed a round trip plus the server's report cadence (combat events flush every tick, so the cadence term is one tick). Too short invalidates good hits on a laggy connection — far worse than the occasional wrong number lingering, because it makes a working hit look broken.

PendingCount

Predicted entries still waiting on the server.

public static int PendingCount { get; }

Property Value

int

Methods

Clear()

Drops every pending prediction without raising rejections.

public static void Clear()

Remarks

For a scene change or disconnect, where the numbers and the characters they were drawn over are both gone. Raising rejections here would ask the display to grey out labels that no longer exist.

Predict(ICharacter, ICharacter, int, Kind, DamageAttributeTemplate, float)

Records and announces a number this client predicted.

public static void Predict(ICharacter source, ICharacter target, int amount, PredictedCombatEvents.Kind kind, DamageAttributeTemplate damageAttribute, float now)

Parameters

source ICharacter

The character that dealt it — this client's own, since only an owner predicts.

target ICharacter

The character the number belongs to.

amount int

The predicted amount.

kind PredictedCombatEvents.Kind

Damage or heal.

damageAttribute DamageAttributeTemplate

Damage type, for colouring. Null for heals and typeless damage.

now float

Current unscaled time, in seconds.

Sweep(float)

Rejects predictions the server never confirmed. Call once per frame or tick.

public static void Sweep(float now)

Parameters

now float

Current unscaled time, in seconds.

TryConfirm(ICharacter, ICharacter, Kind, int)

Consumes a pending prediction matching an arriving server report.

public static bool TryConfirm(ICharacter source, ICharacter target, PredictedCombatEvents.Kind kind, int occurrences = 1)

Parameters

source ICharacter

The attacker named by the report. A report with none matches nothing.

target ICharacter

The character named by the report.

kind PredictedCombatEvents.Kind

Damage or heal.

occurrences int

How many separate hits the report's amount was merged from — CombatEventBroadcast.Occurrences. That many pending predictions are settled, because the caster drew one label per hit while the server sent one message for all of them. Values below one are treated as one.

Returns

bool

True when this report was already drawn as a prediction.

Remarks

Matched on SOURCE, target and kind, oldest first. The source is what makes this a pairing rather than a guess: a combat report is broadcast to everyone observing the victim, so with source ignored any other player's hit on the same target consumed this client's pending entry. Two players on one mob was enough — the other player's real number was swallowed (the caller draws nothing on a match) and this client's own report, arriving to find no pending entry left, was then drawn a second time.

Deliberately NOT matched on the amount. The two numbers agree whenever the RNG states agree, but a transient divergence would otherwise leave the prediction unmatched and produce the worst outcome available: the predicted number greyed out AND the server's number drawn beside it, for a hit that landed. Trusting the pairing and letting the amount differ is the lesser error.

The caller draws the server's number only when this returns false — that is, only when the report settled NOTHING. A report that settles some but not all of what it claims does not produce a second label: the numbers already on screen are the caster's own predictions of the same hits, and drawing the server's merged total beside them would show the damage twice.

Events

OnPredicted

Raised when this client predicts a combat number. The display draws it immediately.

public static event Action<long, ICharacter, int, PredictedCombatEvents.Kind, DamageAttributeTemplate> OnPredicted

Event Type

Action<long, ICharacter, int, PredictedCombatEvents.Kind, DamageAttributeTemplate>

Remarks

The long is a handle the display keeps, so a later OnPredictionRejected can name the number to grey out.

OnPredictionConfirmed

Raised when the server's report confirmed a predicted number. Nothing needs redrawing — the number on screen was already right — but the display holds a handle per prediction so it can grey one out later, and without this it would only ever release the handles for predictions that turned out WRONG. A session's worth of correct hits leaked one entry each.

public static event Action<long> OnPredictionConfirmed

Event Type

Action<long>

OnPredictionRejected

Raised when a predicted number went unconfirmed past the window. The display should mark it invalid rather than deleting it — a number that vanishes reads as a rendering glitch, one that greys out reads as "that did not land".

public static event Action<long> OnPredictionRejected

Event Type

Action<long>