From 3b49e4483ffe497bf5b6d3674786c012b170eaf7 Mon Sep 17 00:00:00 2001 From: WarpZone Date: Wed, 10 Jun 2026 21:59:49 +0200 Subject: [PATCH] Document replication definition files and the unified player/prop update path Add a 'Replication definition files' section explaining the per-type _def.json whitelist, the replication zones (distance/frequency), and two common pitfalls (silently-dropped unlisted properties, and keeping scenename/ position/parent_id in the same zone so a prop is not spawned at the world origin). Note that the player is now just an object of type 'player' (no player-specific update path) and add a 'Spawning a prop from the game server' section for spawn_prop_authoritative. Fix a copy-paste error in the player example. --- docs/networkGame/props_player.md | 91 +++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/docs/networkGame/props_player.md b/docs/networkGame/props_player.md index 6adf386..418547d 100644 --- a/docs/networkGame/props_player.md +++ b/docs/networkGame/props_player.md @@ -5,6 +5,18 @@ sidebar_position: 2 # Props and player network management +The player and every generic prop replicate their state to nearby players through the +**same** mechanism: the game server sends property updates to Horizon, which keeps an +authoritative copy of each object and forwards the changes to the clients in range. The +player is simply an object of type `player` — there is no longer a player-specific update +path. + +Which properties are actually replicated (and how far / how often) is **not** decided in +your GDScript: it is declared in a per-type **definition file**. See +[Replication definition files](#replication-definition-files) below — read +it first, because a property that is not declared there will silently never reach the +clients. + ## Player management ### Update properties @@ -45,9 +57,15 @@ To do that, call the function *server_send_properties_to_client*. For example: ``` -client_send_action_to_server({"health": 80}) +server_send_properties_to_client({"health": 80}) ``` +:::warning[Declare the property first] +`health` will only reach the clients if it is listed in the player definition file +(`player_def.json`). A property that is not whitelisted there is dropped silently. See +[Replication definition files](#replication-definition-files). +::: + #### Client receives update The client receives all properties modified in the function *client_channel_data_update*, such as the new position and health. @@ -195,3 +213,74 @@ Be careful, the int value sent by the server is a float when it arrives, so you To delete a prop, the function *_exit_tree* sends the signal to the client, and the client deletes the scene; it's automatic! + +## Replication definition files + +Emitting `hs_server_prop_update` (props) or `server_send_properties_to_client` (player) is +**not enough** for a property to reach the clients. Horizon only replicates the properties +that are **declared** for that object type, in a definition file. + +These files live in the Horizon plugins, one per object type: + +``` +horizonserver/ds_genericprops/props/_def.json +``` + +where `` is the prop's `type_name` (e.g. `box`, `miningrock`, `player`). + +Example (`box_def.json`): + +```json +{ + "channels": [ + { + "zone": 0, + "distance": 150.0, + "frequency": 30.0, + "properties": ["position", "rotation", "opened", "parent_id"] + }, + { + "zone": 6, + "distance": 153.0, + "frequency": 3.0, + "properties": ["scenename"] + } + ] +} +``` + +Each entry in `channels` is a replication **zone**: + +- `distance` — clients within this radius (meters) of the object receive this zone's properties. +- `frequency` — how many times per second this zone is replicated (use a high rate for fast-changing data like `position`, a low rate for rarely-changing data). +- `properties` — the **whitelist**: only these property names are replicated. + +:::danger[The whitelist is silent] +A property you send that is **not** listed in any zone is dropped without any error. The +client simply keeps its default value (empty string, `0`, ...). Symptom: the object behaves +correctly on the server but is wrong on the other clients. Whenever you add a new replicated +property, **add it to the type's `_def.json`** (and rebuild Horizon). +::: + +:::warning[Keep creation properties in the same zone] +A client instantiates an object as soon as it receives its `scenename`. If `scenename` is in +a farther zone than `position`/`parent_id`, a client standing *between* the two distances +receives the scene **without** its placement and spawns the object at the world origin +`(0,0,0)`. Keep `scenename`, `position` and `parent_id` within the **same** `distance`. +::: + + +## Spawning a prop from the game server + +If the game server creates a prop at runtime (not through the normal spawn flow), registering +it in Horizon is **not** enough: Horizon's replication is players-only and does not echo the +object back to the game server, so the prop would have no server-side body (it floats / can't +be interacted with until a reconnect reloads it from persistence). + +Create it **both** in Horizon (for the other clients) **and** locally on the game server. The +helper does both: + +``` +NetworkOrchestrator.spawn_prop_authoritative(data) # data must hold "uuid" and "type" +``` +