Menu
Lumberyard
Developer Guide (Version 1.11)

Marshalling

Data is written to the network using WriteBuffer, and data received is read using ReadBuffer. Each buffer specifies the endianness used.

All data marshalling, whether for a DataSet or RPC, is written using a specialization of the Marshaler type. There are a number of pre-defined marshalers for fundamental types (int32, uint16, bool, float, etc), as well as other common types like containers and bitfields.

Marshalers and read/write buffers have a close relationship. A marshaler reads or writes its data types from or to the buffer. If the type is a complex type like a class or container, then that marshaler marshals each of its fields with nested marshalers. The nested invocation of marshaler types continues until a fundamental type is written to the buffer with the endianness of the network. Additional custom marshalers can be implemented to support custom types or to perform domain-based compression. Default marshalers are implemented through template specialization.

The base Marshaler class in GridMate follows.

Copy
namespace GridMate { template<typename T> class Marshaler { public: void Marshal(WriteBuffer& wb, const T& value); void Unmarshal(T& value, ReadBuffer& rb); }; }

If a Marshaler instance is not specified with the data set or RPC declaration, the template specialization is used.

Implementation of the default marshaler for AZCore's Vector3 math type can be found in Code/Framework/GridMate/GridMate/Serialize/MathMarshal.h:

Copy
namespace GridMate { template<> class Marshaler<AZ::Vector3> { public: typedef AZ::Vector3 DataType; static const AZStd::size_t MarshalSize = sizeof(float) * 3; void Marshal(WriteBuffer& wb, const AZ::Vector3& vec) const { Marshaler<float> marshaler; marshaler.Marshal(wb, vec.GetX()); marshaler.Marshal(wb, vec.GetY()); marshaler.Marshal(wb, vec.GetZ()); } void Unmarshal(AZ::Vector3& vec, ReadBuffer& rb) const { float x, y, z; Marshaler<float> marshaler; marshaler.Unmarshal(x, rb); marshaler.Unmarshal(y, rb); marshaler.Unmarshal(z, rb); vec.Set(x, y, z); } }; }

Markers

Notice the declaration of MarshalSize above. WriteBuffer supports the concept of markers. A marker is a placeholder that can be inserted into the buffer, so its value can be filled after additional data is written to the buffer. This is useful for prepending a length field in front of the actual data. Markers require that the data that is inserted be of fixed length, and MarshalSize is used to query this length. Therefore, marshalers that write data to the marker need to declare a valid MarshalSize.

Buffers

Write Buffers

Write buffers are backed by the following three types of allocation schemes:

Dynamic – Dynamically allocated and automatically grown

Static – Fixed size, allocated on the stack

Static In Place – Uses another buffer as its backing store

By default, the write function uses the default marshaler for the data type, but you can override the marshaler to create a custom marshaler.

There are two ways to write a type to a network buffer:

1) The following example uses the default marshaler for the type passed into Write(). In this example, the float marshaler is used.

Copy
WriteBuffer wb; wb.Write(1.0f);

2) The following example uses the HalfMarshaler, which compresses the float by half.

Copy
WriteBuffer wb; wb.Write(1.0f, HalfMarshaler());

Read Buffers

Read buffers have built-in overflow detection and do not read any data fields after the end of the buffer has been reached. You can check this by looking at the return value of the Read method. Note that if data isn’t read for a given value, then the value is left uninitialized.

Predefined Marshalers

GridMate includes the following predefined marshalers:

Fundamental C++ Types

Floating point Misc Unsigned Signed

float

double

char

bool

enum (specify marshaled size by inheriting enum from a type)

AZ::u8

AZ::u16

AZ::u32

AZ::u64

AZ::s8

AZ::s16

AZ::s32

AZ::s64

Container Types

Sequence Associative Explicit Marshalers

vector

list

string

map

set

unordered_map

unordered_set

multimap

multiset 

ContainerMarshaler

MapContainerMarshaler

(Use these marshalers when the subtypes of the container require a non-default marshaler)

Utility Types

Name Description
ConversionMarshaler<SerializedType, OriginalType> Performs static casts between SerializedType (type on the wire) and OriginalType (type declared in user code).
AZ::Crc32 A CRC32 value.
AZStd::bitset A class for arbitrary flags.
AZStd::pair A std pair class. Implicitly used by the map, unordered_map, and multimap marshalers.
AZ::Aabb An axis aligned bounding box.
AZStd::chrono::duration A time duration in 32 bit milliseconds.
GridMate::UnionDataSet A type safe tagged union designed for network transmission.

Compression Types

Name Description
Float16Marshaler Compresses a float32 to float16.
HalfMarshaler Compresses a float to half precision.
IntegerQuantizationMarshaler<Min, Max, Bytes> Quantizes an integer in the range [Min, Max] to the number of bytes specified in Bytes.

Custom Marshalers

Creating a custom data marshaler is as simple as specializing the GridMate::Marshaler type, and implementing the expected Marshal and Unmarshal methods. If the data written is constant size, adding the member MarshalSize allows you to use the marshaler in scenarios where fixed sizes are required (such as markers).

Fixed Size Custom Marshaler

The following is an example of a fixed size custom marshaler.

Copy
namespace GridMate { template<> class Marshaler<MyClass> { public: static const AZStd::size_t MarshalSize = sizeof(m_field1) + sizeof(m_field2); void Marshal(GridMate::WriteBuffer& wb, const MyClass& value) const { wb.Write(value.m_field1); wb.Write(value.m_field2); } void Unmarshal(MyClass& value, ReadBuffer& rb) const { rb.Read(value.m_field1); rb.Read(value.m_field2); } }; }