Class CommandBuilder<T>
Declares a command type: what it is called on the wire, how fast a session may send it, who may send it, and what it must look like to be worth decoding.
public sealed class CommandBuilder<T> where T : unmanaged
Type Parameters
TThe command type: an unmanaged struct, declared in an assembly a client can reference without the engine.
- Inheritance
-
CommandBuilder<T>
- Inherited Members
Remarks
Every public instance field of T travels, whether or not the declaration mentions it (01-model § 7: "fields default to their
raw type"). Field<TField>(Expression<Func<T, TField>>, Codec, string) OVERRIDES how one of them travels; Ignore<TField>(Expression<Func<T, TField>>) keeps one off the wire. Silence declares nothing,
deliberately: a command whose author forgot a field would otherwise arrive zeroed on a server that had no way to say so.
The struct's layout does not define the wire. Fields travel in the catalog's canonical order and are decoded one at a time, so reordering members of the C# struct is not a silent protocol break — which is exactly what canonicalization exists to prevent.
The rate limit is enforced on the transport thread, before the ring, so a client flooding a command type costs the tick nothing.
Methods
Coalesce(CommandCoalesce)
Sets how arrivals are delivered into a tick.
public CommandBuilder<T> Coalesce(CommandCoalesce mode)
Parameters
modeCommandCoalesceQueued, or newest-per-session.
Returns
- CommandBuilder<T>
This builder.
Field<TField>(Expression<Func<T, TField>>, Codec, string)
Overrides how one of the command's fields travels — a position quantized over the grid, a reference sent as a netId — instead of its raw type.
public CommandBuilder<T> Field<TField>(Expression<Func<T, TField>> selector, Codec codec, string name = null)
Parameters
selectorExpression<Func<T, TField>>Selects the field.
codecCodecHow the value travels.
namestringThe wire name. null takes the field's own name.
Returns
- CommandBuilder<T>
This builder.
Type Parameters
TFieldThe field's type.
Remarks
It is an override, not the thing that makes the field travel: a field nobody mentions travels under the codec its CLR type defaults to. What this buys
is the cases a type cannot imply — a pair of floats that is a world position, a u32 that is a netId, a range that deserves 8 bits instead of 32.
Exceptions
- InvalidOperationException
The selector names something that is not a public instance field of
T, or that field is already declared or ignored.
Ignore<TField>(Expression<Func<T, TField>>)
Keeps one of the command's fields off the wire: scratch space, a field the server fills in, a value the client has no business sending.
public CommandBuilder<T> Ignore<TField>(Expression<Func<T, TField>> selector)
Parameters
selectorExpression<Func<T, TField>>Selects the field.
Returns
- CommandBuilder<T>
This builder.
Type Parameters
TFieldThe field's type.
Remarks
It takes a verb because silence must not mean "not replicated". Every other field travels by default, so leaving one out of the declaration is indistinguishable from forgetting it — and the failure that produces is a field that arrives zeroed for ever, discovered by a player. Saying so costs one line and makes the omission reviewable.
Exceptions
- InvalidOperationException
The selector names something that is not a public instance field of
T, or that field already carries a Field<TField>(Expression<Func<T, TField>>, Codec, string) declaration.
Name(string)
Overrides the wire name, which is the type's own name by default.
public CommandBuilder<T> Name(string name)
Parameters
namestringThe wire name.
Returns
- CommandBuilder<T>
This builder.
Precheck(CommandPrecheck<T>)
Attaches the syntactic pre-check run before the command enters the tick.
public CommandBuilder<T> Precheck(CommandPrecheck<T> precheck)
Parameters
precheckCommandPrecheck<T>The check.
Returns
- CommandBuilder<T>
This builder.
Remarks
The adapter is captured here, where T is a compile-time argument, because the ingress path that calls the check holds only a
Type and raw payload bytes. Reaching a typed delegate from a Type would take MakeGenericType over a value type,
which is the AOT blocker class #409 names; a static lambda here is one cached instance per command type and no reflection at all.
Rate(int, int)
The per-session token bucket for this command type.
public CommandBuilder<T> Rate(int perSecond, int burst)
Parameters
perSecondintSustained rate, above zero.
burstintBucket depth — how many may arrive at once after a quiet period. At least
perSecond.
Returns
- CommandBuilder<T>
This builder.
Roles(params SessionRole[])
The roles allowed to send this command. Declaring none accepts every role.
public CommandBuilder<T> Roles(params SessionRole[] roles)
Parameters
rolesSessionRole[]The accepted roles.
Returns
- CommandBuilder<T>
This builder.