Generated code
The C++ schema-generated code consists of two main parts: data classes and component classes.
Data classes
Data classes are used to represent data at rest and correspond to the schemalang type
definitions.
A data class is generated for each type defined in the schema, with fields corresponding to each field in the schema definition.
Data representation
- Strings are represented as UTF-8 encoded
std::string
members. Make sure to use this same encoding when updating a component. list<T>
fields are represented as aworker::List<T>
of the repeated type. This type is very similar tostd::vector<T>
and has most of the methods you’d expect.map<Key, Value>
fields are represented asworker::Map<Key, Value>
. This type is very similar tostd::unordered_map<Key, Value>
and has most of the methods you’d expect.The biggest difference is it has a well-defined iteration order, which is insertion order.
option<T>
fields are represented asworker::Option<T>
. First, check if the option contains a value with!option.empty()
orif (option) { ... }
, then dereference the option to access the contained value. This type is very similar to thestd::optional<T>
type introduced in C++17.
Component classes
Component classes correspond to schemalang component
definitions and contain metadata and classes
for sending and receiving component data (updates, command requests and command responses).
For each component, we generate:
a metaclass.
These metaclasses are used when referring to specific components when using the API - every template parameter
T
expects a metaclass argument.an
Update
class nested inside the metaclass.This has an optional field for each field in the component (since it represents a diff).
command metaclasses nested inside the metaclass, as
SomeComponent::Commands::SomeCommand
.These work in a similar way to component metaclasses, but for command-related functions.
Example
Given the following simple schema (in package example
):
type StatusEffect {
string name = 1;
int32 multiplier = 2;
}
component Creature {
id = 12345;
int32 health = 1;
list<StatusEffect> effects = 2;
}
The generated code will contain a data class StatusEffect
corresponding to type defined in the
schema. For the creature
component, a Creature
metaclass containing a Creature::Update
class
will be generated, alongside a CreatureData
data class.
The Creature
metaclass can be passed as a template parameter to any templated API function, for
example to send an update for the creature
component. Note that the Creature
metaclass is also
used with the worker::Entity
class (which the
View
uses to represent entities) to inspect
component data “at rest”.
Here are some ways that these classes can be used with the API:
void GeneratedCodeExamples(worker::Connection& connection) {
worker::Dispatcher dispatcher(MyComponents());
dispatcher.OnAuthorityChange<example::Creature>([&](const worker::AuthorityChangeOp& op) {
if (op.Authority == worker::Authority::kAuthoritative) {
// We were granted authority over the creature component. Send an update.
example::Creature::Update update;
update.set_health(10);
connection.SendComponentUpdate<example::Creature>(op.EntityId, update);
} else {
// Authority was revoked.
}
});
dispatcher.OnComponentUpdate<example::Creature>(
[&](const worker::ComponentUpdateOp<example::Creature>& op) {
const example::Creature::Update& update = op.Update;
if (update.effects()) {
auto effects = update.effects().data();
for (const auto& effect : *effects) {
std::cout << op.EntityId << " has effect " << effect.name() << " with multiplier "
<< effect.multiplier() << std::endl;
}
}
});
}