Table of Contents

Class AbilityObjectObserverBroadcastSerializers

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Hand written wire formats for the three per-object ability broadcasts: AbilityObjectDestroyedBroadcast, AbilityObjectHitBroadcast and AbilityObjectRedirectBroadcast.

public static class AbilityObjectObserverBroadcastSerializers
Inheritance
AbilityObjectObserverBroadcastSerializers
Inherited Members

Remarks

These are per-hit and per-object messages paid once per observer, and the generated serializer wrote every field of every one of them packed and unconditionally. Two things were wrong with that.

  • Packing a full-entropy word costs a byte rather than saving four. FishNet's packed 32-bit form is a seven-bit-per-byte varint, so a value that uses the whole range takes FIVE bytes where the unpacked form takes four. ContainerID is seed ^ (tick * 1000003) (AbilityContainerAllocator.ComputeContainerId) and PackedHeading is an AimDirectionCompression word; both are unpacked here. Object ids, the ability id and the absolute tick stay packed, because those genuinely are small.
  • A hit message has two shapes and was written as one. A deflected hit is answered by AbilityController.OnAbilityObjectHitBroadcast reading the heading and returning, so the point, the normal, the victim and the direct-impact flag were written and never read — twenty-five bytes per deflected hit per observer. A header byte now names the shape, in the style of WriteAbilityActivatedBroadcast(Writer, AbilityActivatedBroadcast).

Discovered by FishNet's codegen through the Write*/Read* naming convention and applied across assemblies because each struct carries [UseGlobalCustomSerializer].

Methods

ReadAbilityObjectDestroyedBroadcast(Reader)

Reads an AbilityObjectDestroyedBroadcast written by the method above.

public static AbilityObjectDestroyedBroadcast ReadAbilityObjectDestroyedBroadcast(this Reader reader)

Parameters

reader Reader

Returns

AbilityObjectDestroyedBroadcast

ReadAbilityObjectHitBroadcast(Reader)

Reads an AbilityObjectHitBroadcast written by the method above.

public static AbilityObjectHitBroadcast ReadAbilityObjectHitBroadcast(this Reader reader)

Parameters

reader Reader

Returns

AbilityObjectHitBroadcast

Remarks

Fields a shape does not carry come back at their defaults — Vector3.zero for the point and the normal, 0 for the deflect heading, false for the impact flags, and 0 for the victim, which is this message's "hit scenery" value rather than the -1 an activation uses for an absent target. That is exactly what the receiving side expects: AbilityController.OnAbilityObjectHitBroadcast branches on Deflected first and only reads the fields the remaining shape carries.

ReadAbilityObjectRedirectBroadcast(Reader)

Reads an AbilityObjectRedirectBroadcast written by the method above.

public static AbilityObjectRedirectBroadcast ReadAbilityObjectRedirectBroadcast(this Reader reader)

Parameters

reader Reader

Returns

AbilityObjectRedirectBroadcast

WriteAbilityObjectDestroyedBroadcast(Writer, AbilityObjectDestroyedBroadcast)

public static void WriteAbilityObjectDestroyedBroadcast(this Writer writer, AbilityObjectDestroyedBroadcast value)

Parameters

writer Writer
value AbilityObjectDestroyedBroadcast

WriteAbilityObjectHitBroadcast(Writer, AbilityObjectHitBroadcast)

Writes an AbilityObjectHitBroadcast in its shape-dependent form.

public static void WriteAbilityObjectHitBroadcast(this Writer writer, AbilityObjectHitBroadcast value)

Parameters

writer Writer
value AbilityObjectHitBroadcast

Remarks

Two shapes, named by the header byte. A deflection carries the heading and nothing else: the receiver applies it and returns before it looks at the point, the normal, the victim or any of the impact flags, so writing them was pure waste on the one hit shape that occurs in bursts. An ordinary impact carries the point, the normal and — only when it is not a scenery hit — the victim id, and carries no heading at all. The block and the end are bits in the header rather than fields, so neither costs the impact shape anything at all.

PackedDeflectHeading is unpacked, like the container id above it, and the shaping is what makes that the right call. The field used to be written on every hit and was 0 on almost all of them, which is the one case the packed form is good at — a single byte. Now it is written only when a deflection actually happened, so every value that reaches the wire is a full-entropy AimDirectionCompression word, which the packed form spends five bytes on.

WriteAbilityObjectRedirectBroadcast(Writer, AbilityObjectRedirectBroadcast)

public static void WriteAbilityObjectRedirectBroadcast(this Writer writer, AbilityObjectRedirectBroadcast value)

Parameters

writer Writer
value AbilityObjectRedirectBroadcast