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:
| Field | Description |
|---|---|
symbols | a list of strings, referenced by index, representing names of fields, serializers, etc.. |
serializers | a list of serializer objects (see below) |
fields | a list of serializer field objects, referenced by index (see below) |
Serializers
Serializers (as the ProtoFlattenedSerializer_t type) have three properties:
| Field | Description |
|---|---|
serializer_name_sym | the symbol index of the serializer name |
serializers | the version of this serializer |
fields | a 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:
| Field | Symbol Index | Optional | Type | Description |
|---|---|---|---|---|
var_name_sym | ✓ | all | the name of this field | |
var_type_sym | ✓ | ✓ | valued | the type of this field, |
bit_count | ✓ | valued | the bit-count for a field | |
low_value | ✓ | valued | a specified lowest value (QF) | |
high_value | ✓ | valued | a specified highest value (QF) | |
encode_flags | ✓ | valued | flags for controlling encoding/decoding behaviour (QF) | |
var_encoder_sym | ✓ | ✓ | valued | a specified encoder type for this field |
field_serializer_name_sym | ✓ | ✓ | table | the name of the field's nested serializer |
field_serializer_version | ✓ | ✓ | table | the version of the field's nested serializer |
polymorphic_types | ✓ | ✓ | poly-table | a list of possible serializer types for a poly table |
Field Layouts
Fields can be modelled in six possible ways:
| Name | Nested | Description |
|---|---|---|
Value | A simple, single value. | |
FixedArray | A fixed size, array of values | |
VariableArray | A variable size (resizable) array of values | |
FixedTable | ✓ | A single nested table |
VariableTable | ✓ | A variable size (resizable) list of nested tables |
PolyTable | ✓ | A 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_STOCKSthen the count should be8 - If the
COUNT == MAX_ABILITY_DRAFT_ABILITIESthen the count should be48 - If the
COUNTis empty string then the count should be1024 - If the
COUNTis 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:
CBodyComponentDCGBaseAnimatingCBodyComponentBaseAnimatingCBodyComponentBaseAnimatingOverlayCBodyComponentBaseModelEntityCBodyComponentCBodyComponentSkeletonInstanceCBodyComponentPointCLightComponentCRenderComponentCPhysicsComponent
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_versionare present) then:- If it's a pointer type, then:
FixedTable
- Otherwise:
VariableTable
- If it's a pointer type, then:
- If the count is present and not
0, and the base-type is notcharthen:FixedArray
- If the base-type is
CUtlVectororCNetworkUtlVectorBasethen: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.