Replication
CompilesReplicationreplication: The server sending a component's changed fields to every client, so they all agree. is the server sending a component’s changed
mutablemutable: A field declared with let mut. The script's running state: it changes while the game runs. fields to every client, so every copy of the component agrees. You set it up once,
in type_created.
Step 1: let the script replicate
Section titled “Step 1: let the script replicate”event export fn type_created() { $net.set_replicated(true, false)}The first argument switches replication on for components of this script. The second decides the default for fields:
false: every mutable replicates unless you say otherwise. The usual choice.true: no mutable replicates unless you opt it in.
A script that never needs the network says $net.set_replicated(false, false): every machine runs its own copy
independently. Most shipped scripts that only drive visuals do this.
Step 2: tune each field
Section titled “Step 2: tune each field”$net.set_field_replicated configures one field by name. It takes twelve values; most are false:
event export fn type_created() { $net.set_replicated(true, false) $net.set_field_replicated("presses", true, false, false, false, false, "", false, 0, 9999, 1, true)}| # | Parameter | For presses |
Meaning |
|---|---|---|---|
| 1 | field |
"presses" |
The mutable’s name. |
| 2 | is_replicated |
true |
Send it at all. false keeps a field server-only. |
| 3 | only_to_owner |
false |
Send only to the user who owns the component. |
| 4 | is_quaternion |
false |
A vec4 that is a rotation. |
| 5 | is_normal |
false |
A vec2/vec3 that is a direction of length 1. |
| 6 | is_time_ms |
false |
An int that is a time in milliseconds. |
| 7 | is_enum |
"" |
For an int holding an enum: the enum’s name. |
| 8 | is_dependency |
false |
For a handle field: the other entity must arrive first. |
| 9–11 | quantize_min, quantize_max, quantize_step |
0, 9999, 1 |
The range a number can take and its smallest step. |
| 12 | fire_event |
true |
Send field_replicated when a new value arrives. |
The engine sends a number using only as much space as its range and step need. A field that
counts to 9999 in steps of 1 is cheap; a float from 0 to 1 in steps of 0.01 is cheap. A value outside its range
can’t be sent faithfully, so pick a range that covers every value the field can take.
Keep a field off the wire with is_replicated false. Use it for bookkeeping only the server needs, like when it last counted
a press:
event export fn type_created() { $net.set_replicated(true, false) $net.set_field_replicated("last_press_ms", false, false, false, false, false, "", false, 0, 0, 0, false)}Step 3: react when a value arrives
Section titled “Step 3: react when a value arrives”With fire_event on, each client gets field_replicated when a new value lands. The first argument is the field’s
name, the second its previous value; the field itself already holds the new one:
let mut presses: int = 0
event export fn field_replicated(field, previous) { if field == "presses" { $debug.print_line("presses went from {} to {}", [previous, presses], false) }}The full script
Section titled “The full script”// press_counter.vscript: counts how many times players have stepped on a// pad, and tells every player's game when the count changes.//// The server owns the count. Players' games never change it; they are sent// each new value and react to it.//// Sits on the same entity as a box_trigger.
// Seconds between counted presses, so standing on the pad counts once.let cooldown_seconds: float = 1.0;
// The count. Replicated: the server's value is sent to every player.let mut presses: int = 0;
// Server only: when the last press was counted.let mut last_press_ms: int = -100000;
event export fn type_created() { // Components of this script replicate. $net.set_replicated(true, false);
// `presses` replicates as a whole number from 0 to 9999, and players' // games get a field_replicated event each time a new value arrives. $net.set_field_replicated("presses", true, false, false, false, false, "", false, 0, 9999, 1, true);
// `last_press_ms` is the server's own bookkeeping; keep it off the wire. $net.set_field_replicated("last_press_ms", false, false, false, false, false, "", false, 0, 0, 0, false);}
event export fn trigger_overlapped(other, unused) { // Every machine sees the overlap; only the server counts it. if !$net.is_server() || !other { return; } let player: Component = $entity.get_hierarchy_singleton("catch_player.vscript", $component.get_entity(other)); if !player { return; } let now: int = $time.get_gameplay_time_ms(); if now - last_press_ms < $time.seconds_to_ms(cooldown_seconds) { return; } last_press_ms = now; presses = presses + 1; $debug.print_line("[press_counter] server counted press {}", [presses], false);}
// On each player's game: a new count arrived from the server.event export fn field_replicated(field, previous) { if field == "presses" { $debug.print_line("[press_counter] count went from {} to {}", [previous, presses], false); }}For programmers
Replication is per-field delta with quantisation driven by quantize_min/max/step; bit width follows from the
range. set_replicated’s second argument flips the default from opt-out to opt-in. Handle fields with
is_dependency order spawn dependencies. field_replicated fires only when fire_event is set, on receivers.