Field Decoders

To parse entity updates, we need to map the fields of the send-table to their respective types within an update. To do this, we create a "field decoder" for each field which can be initialised from the parameters found from the send-table.

Note: Within this guide, we will refer to the 'field type' as a "decoder".

Decoder Types

First, let's define the possible types of decoders:

We've listed all the decoders we currently support, along with their wire type and the type they decode to.

Decoder NameWire TypeTypeDescription
Booleanbit_boolbool
Signedbit_varint_32 int32
Unsignedbit_varuint_32uint32
Componentbit_component_uintuint32
AmmoCountbit_ammocountuint32
Unsigned64bit_varuint_64uint64
FixedUnsigned64bit_uint64_leuint64
Stringbit_string_null_termstring
NoScalebit_float32_noscalefloat32
Coordbit_float32_coordfloat32
SimTimebit_simulation_timefloat32
RuneTimebit_rune_timefloat32
Qfloatquantised_float *float32*maps to a quantised float decoder
QAnglePrecbit_qangle_precisefloat32
QAngleFixedbit_qangle_fixedfloat32
QAngleCoordbit_qangle_coordfloat32
Vector3Normalbit_vec3_normalfloat32[3]
Vector2[F]F x2 *float32[2]*calls the float factory, creates a vector of degree 2
Vector3[F]F x3 *float32[3]*calls the float factory, creates a vector of degree 3
Vector4[F]F x4 *float32[4]*calls the float factory, creates a vector of degree 4
Vector6[F]F x6 *float32[6]*calls the float factory, creates a vector of degree 6

Matching Decoders

When matching a field to a decoder, there are three methods:

  • Factory: A decoder with logical mapping and parameter initialisation
  • Overrides: A overridden type, controlled by another parameter (such as var_name)
  • Mapping: A simple name to decoder mapping

Factories

As stated before, not all field types map to a decoder one-to-one. Factories map a type to multiple types of decoders depending on their parameters.

Factories can also be called other factories. For example, the float factory can call the Quantised Float factory, or the vector factory calls the float factory.

Quantised Float Factory

  • If bit_count != nil and bit_count < 32:
    • NoScale
  • Otherwise, create a quantised float decoder from the parameters:
    • bit_count (if not present then default to 0)
    • encode_flags (if not present then default to 0)
    • low_value
    • high_value

Float Factory

  • If encoder == "coord":
    • CoordType
  • If encoder == "simtime":
    • SimTime
  • If encoder == "runetime":
    • RuneTime
  • If bit_count == nil or bit_count < 32:
    • NoScale
  • Otherwise, call Quantised Float Factory

Unsigned64 Factory

  • If encoder == "fixed64"
    • Vector3[Normal]
  • Otherwise
    • Unsigned64

QAngle Factory

  • If encoder == "qangle_precise"
    • QAnglePrec
  • If bits != nil and bits != 0:
    • QAngleFixed
  • Otherwise
    • QAngleCoord

Vector3 Factory

  • If encoder == "normal"
    • Vector3Normal
  • Otherwise call VectorN Factory
    • vector(3)

VectorN Factory

In this documentation, we reference the vector factory as Vector[N] Factory where N dictates the degree (size) of the vector.

The vector factory calls Float Factory to create its inner type, and on the wire reads this N times to get a resulting vector.

Overrides

Some field types can be overridden, there is currently one case:

ConditionDecoder
`var_name == "m_iClip1"AmmoCount

Mapping

This table contains a mapping to a field's decoded type to its respective factory or decoder.

This table should be applied in sequential order to prevent any issues with overlap between factories and mappings.

Base TypeFactoryDecoder
float32Float
CNetworkedQuantizedFloatQuantised Float
uint64Unsigned 64
CStrongHandleUnsigned 64
VectorVector3
Vector2DVector[2]
Vector4DVector[4]
QuaternionVector[4]
CTransformVector[6]
QAngleQAngle



boolBoolean
 
int8Signed
int16Signed
int32Signed
HSequenceSigned
CEntityIndexSigned
NPC_STATESigned
AmmoIndex_tSigned
TakeDamageFlags_tSigned
StanceType_tSigned
 
RagdollBlendDirectionUnsigned
BeamType_tUnsigned
EntityDisolveType_tUnsigned
PrecipitatonType_tUnsigned
BeamClipStyle_tUnsigned
SharpSolid_tUnsigned
ShatterPanelModeUnsigned
gender_tUnsigned
item_definition_index_tUnsigned
uint8Unsigned
uint16Unsigned
uint32Unsigned
CHandleUnsigned
ColorUnsigned
CUtlStringTokenUnsigned
EHandleUnsigned
CEntityHandleUnsigned
CGameSceneNodeHandleUnsigned
CStrongHandleUnsigned
AttachmentHandle_tUnsigned
MoveCollide_tUnsigned
MoveType_tUnsigned
RenderMode_tUnsigned
RenderFx_tUnsigned
SolidType_tUnsigned
SurroundingBoundsType_tUnsigned
ModelConfigHandle_tUnsigned
WeaponState_tUnsigned
DoorState_tUnsigned
ValueRemapperInputType_tUnsigned
ValueRemapperOutputType_tUnsigned
ValueRemapperHapticsType_tUnsigned
ValueRemapperMomentumType_tUnsigned
ValueRempapperRatchetType_tUnsigned
PointWorldTextJustifyHorizontal_tUnsigned
PointWorldTextJustifyVertical_tUnsigned
PointWorldTextReorientMode_tUnsigned
PoseController_FModType_tUnsigned
itemid_tUnsigned
style_index_tUnsigned
attributeprovidertypes_tUnsigned
DamageOptions_tUnsigned
MaterialModifyMode_tUnsigned
CSWeaponModeUnsigned
ESurvivalSpawnTileStateUnsigned
SpawnStage_tUnsigned
ESurvivalGameRuleDecision_tUnsigned
RelativeDamagedDirection_tUnsigned
CSPlayerStateUnsigned
MedalRank_tUnsigned
CSPlayerBlockingUseAction_tUnsigned
MoveMountingAmount_tUnsigned
QuestProgress::ReasonUnsigned
tablet_skin_state_tUnsigned
ScreenEffectType_tUnsigned
 
charString
CUtlStringString
CUtlSymbolLargeString
 
GameTime_tNoScale
 
CBodyComponentComponent
CPhysicsComponentComponent
CLightComponentComponent
CRenderComponentComponent

Lookup Methodology

Mapping a field type to a decoder is done by a lookup method. There are two variants of this:

Lookup Field Decoder

This is the most common use of decoding a field type. It is implemented as such:

  1. Check factories
  2. Check overrides
  3. Check mapping
  4. Otherwise, Unsigned

This will be referenced as lookup_field_decoder

Lookup Field Inner Decoder

This is the less often used decoder, are used for decoding the inner child elements of an array.

Unlike Field Type, Base Type is actually the inner GENERIC type, not the outer type (because it will always be an array)

It is implemented as such:

  1. Check if we have a generic type.
    • Fatally Error if this is not the case.
  2. Check factories
  3. Check mapping.
  4. Otherwise, Unsigned

Note: we do not need to check overrides in this implementation.

This will be referenced as lookup_field_inner_decoder