Table of Contents

Class AimDirectionCompression

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Packs an aim direction into 32 bits as a quantised yaw/pitch pair, and — more importantly — exposes the quantisation itself so a producer can commit to the value it is about to send.

public static class AimDirectionCompression
Inheritance
AimDirectionCompression
Inherited Members

Remarks

Why this exists. The ability system is deterministic: every peer replays the same input stream through the same simulation and derives the same ability objects locally, so the inputs must be bit-identical everywhere. Aim used to travel as a UnityEngine.Quaternion through FishNet's WriteQuaternion32, which is lossy. The owning client simulated with its exact camera rotation while the server and every observer simulated with the decoded one — so an owner's predicted shot and the server's authoritative shot diverged by the quantisation error on every single cast.

The rule this type enforces. Quantise before you predict, never after. A producer calls Quantize(Vector3) and stores the result as the input; the wire then carries a value that is already exactly representable, so decoding it reproduces what the producer simulated with. Decode(uint) of Encode(Vector3) is the identity on any value that came out of Quantize(Vector3), which is what the round-trip tests assert.

Why direction rather than a rotation. Aim is only ever consumed as a direction — KCCController.SetInputs takes rotation * Vector3.forward to build the movement basis, and the ability path takes the same forward as its trace direction. Carrying a full rotation shipped a degree of freedom (roll) that nothing reads and that made the value harder to represent exactly.

Resolution is 360/65536 ≈ 0.0055° of yaw and 180/65535 ≈ 0.0027° of pitch (yaw wraps, so its last step joins the first; pitch spans pole to pole inclusive, so it has one fewer interval) — about 5 mm of lateral error at 50 m, comfortably below the precision any aiming decision depends on.

Fields

FallbackDirection

Direction used when asked to encode a zero-length or non-finite vector.

public static readonly Vector3 FallbackDirection

Field Value

Vector3

Remarks

A deterministic fallback matters more than which direction it is: a NaN reaching the simulation would diverge peers permanently, whereas everyone agreeing on "forward" is merely wrong in a visible, debuggable way.

QuantizedFallbackDirection

FallbackDirection after a round trip through the packer — the vector every peer actually ends up simulating when an aim is missing.

public static readonly Vector3 QuantizedFallbackDirection

Field Value

Vector3

Remarks

Not the same vector as FallbackDirection: encoding rounds the pitch to index 32768 rather than its exact half-step, so the decoded forward carries a tiny non-zero y. Substituting the RAW fallback has the substituting peer simulate a direction the wire cannot carry while every peer that received it simulates the decoded one — the same quantise-at-the-producer rule the aim itself follows, in the one case where nothing else is going right anyway. Computed once; Quantize is a full round trip.

Methods

Decode(uint)

Unpacks a direction previously produced by Encode(Vector3).

public static Vector3 Decode(uint packed)

Parameters

packed uint

The packed representation.

Returns

Vector3

A unit direction.

Encode(Vector3)

Packs a direction into 32 bits: low 16 bits yaw, high 16 bits pitch.

public static uint Encode(Vector3 direction)

Parameters

direction Vector3

Direction to encode. Need not be normalised.

Returns

uint

The packed representation.

Quantize(Vector3)

Rounds a direction onto the wire's representable set.

public static Vector3 Quantize(Vector3 direction)

Parameters

direction Vector3

Raw direction.

Returns

Vector3

The direction the wire will carry.

Remarks

This is the call a producer makes before storing aim as input. Feeding the simulation the quantised value — rather than the raw one it happens to hold locally — is what keeps the owner, the server and every observer simulating identical input.

ToRotation(Vector3)

Rebuilds a rotation from an aim direction, for the code paths that still want one.

public static Quaternion ToRotation(Vector3 direction)

Parameters

direction Vector3

A direction, ideally already quantised.

Returns

Quaternion

A rotation whose forward is direction.

Remarks

Roll is not carried — nothing reads it — so this picks a reference up-axis and falls back to a second one when the direction is close to vertical, where UnityEngine.Quaternion.LookRotation(UnityEngine.Vector3, UnityEngine.Vector3) is otherwise degenerate. Deterministic for a given direction, which is the property that matters; a camera pitched to exact vertical would lose only its roll, which no consumer reads.