Expanding Serializers & Fields

Send-tables can be found in two places, within a demo frame (CDemoSendTables) or in a packet (CSVCMsg_FlattenedSerializer). These contain the entire serializer set for the demo.

They are sent in a flattened format, which has three properties:

FieldDescription
symbolsa list of strings, referenced by index, representing names of fields, serializers, etc..
serializersa list of serializer objects (see below)
fieldsa list of serializer field objects, referenced by index (see below)

Serializers

Serializers (as the ProtoFlattenedSerializer_t type) have three properties:

FieldDescription
serializer_name_symthe symbol index of the serializer name
serializersthe version of this serializer
fieldsa list of indexes, referring to the fields in this serializer

Serializers are the basic building block of the send-tables. They can be referenced (nested) inside other serializers, and some are entity class types. (We'll discuss this later)

Note: multiple serializers with the same name can exist but will have different versions.

When getting a serializer for a class, only one (determinate) serializer will exist with the given name.

Fields

Fields are the most complex part of the send-tables.

Each field has a model and one or more decoded types, which are derived from a set of parameters.

Here are the notable parameters from the ProtoFlattenedSerializerField_t type:

FieldSymbol IndexOptionalTypeDescription
var_name_symallthe name of this field
var_type_symvaluedthe type of this field,
bit_count valuedthe bit-count for a field
low_valuevalueda specified lowest value (QF)
high_valuevalueda specified highest value (QF)
encode_flagsvaluedflags for controlling encoding/decoding behaviour (QF)
var_encoder_symvalueda specified encoder type for this field
field_serializer_name_symtablethe name of the field's nested serializer
field_serializer_versiontablethe version of the field's nested serializer
polymorphic_typespoly-tablea list of possible serializer types for a poly table

Field Layouts

Fields can be modelled in six possible ways:

NameNestedDescription
ValueA simple, single value.
FixedArrayA fixed size, array of values
VariableArrayA variable size (resizable) array of values
FixedTableA single nested table
VariableTableA variable size (resizable) list of nested tables
PolyTableA single table that can change type (polymorphic)

Parsing Field Type

The var_type_sym field contains a string that describes the type of the field. It can be used to derive the layout of the field as well as the other parameters we will use later in the send-table process. This value is a string and can be parsed using the following grammar:

TYPE      ::= BASE_TYPE GENERIC? POINTER? COUNT?
BASE_TYPE ::= /[a-zA-Z0-9]+/
GENERIC   ::= < TYPE > 
POINTER   ::= /\*/
COUNT     ::= [ /[0-9]*/ ] 

Here is an example regex you can use:

([^<\[*]+)(<\s(.*)\s>)?(\*)?(\[(.*)])?

You will use BASE_TYPE, GENERIC POINTER and, COUNT for multiple times in the send-table process. (Not just for determining the field layout)

Field Type Edge Cases

When processing the COUNT component:

  • If the COUNT == MAX_ITEM_STOCKS then the count should be 8
  • If the COUNT == MAX_ABILITY_DRAFT_ABILITIES then the count should be 48
  • If the COUNT is empty string then the count should be 1024
  • If the COUNT is a number, then it should be parsed.
  • Otherwise, the count should be ignored.

If the base-type is one of the following, it's deemed also as a pointer type:

  • CBodyComponentDCGBaseAnimating
  • CBodyComponentBaseAnimating
  • CBodyComponentBaseAnimatingOverlay
  • CBodyComponentBaseModelEntity
  • CBodyComponent
  • CBodyComponentSkeletonInstance
  • CBodyComponentPoint
  • CLightComponent
  • CRenderComponent
  • CPhysicsComponent

Mapping Field Type to Layouts

Once extracting components from the var-type, we can map the field to its model:

  • If the field has poly-types, then:
    • PolyTable
  • If the field has a serializer (field_serializer_name_sym && field_serializer_version are present) then:
    • If it's a pointer type, then:
      • FixedTable
    • Otherwise:
      • VariableTable
  • If the count is present and not 0, and the base-type is not char then:
    • FixedArray
  • If the base-type is CUtlVector or CNetworkUtlVectorBase then:
    • VariableArray
  • Otherwise
    • Value

Next Steps

Writers Note:

At this point I would recommend introducing a "constructor" type to contain all these parameters (including the field-model). This will make it easier to map field decoders and to construct the field. We called ours FieldConstructor, but it could be anything.

The next step is to build out decoders for fields.