diff --git a/docs/networkGame/props_player.md b/docs/networkGame/props_player.md index 6adf386..7c860cd 100644 --- a/docs/networkGame/props_player.md +++ b/docs/networkGame/props_player.md @@ -193,5 +193,58 @@ Be careful, the int value sent by the server is a float when it arrives, so you ### Delete prop -To delete a prop, the function *_exit_tree* sends the signal to the client, and the client deletes the scene; it's automatic! +Deletion is **server-authoritative**: a prop is removed by freeing its node **on the Godot +server** — never directly by a client. When the node leaves the tree, its *_exit_tree* emits +*hs_server_prop_delete* and the rest is automatic: + +1. The Godot server sends a `props/delete_object` message to Horizon. +2. Horizon removes the object from GORC, so every nearby client receives a zone-exit and + despawns the scene (the prop disappears for everyone). +3. Horizon also forwards the deletion to the **persistence** service, which removes the row + from the database — so the prop does **not** respawn after a restart. + +#### Triggering a deletion (server side) + +Free the prop node on the server. For example, the mining depot consumes a deposited rock: + +``` +func _collect_rock(rock: Node) -> void: + # ... accumulate stats ... + rock.queue_free() # _exit_tree -> hs_server_prop_delete -> GORC + database +``` + +#### Requesting a deletion from a client + +A client never deletes a prop itself. It sends an action to the server (the same channel as +any other action, see *Update properties* above); the server validates it and triggers the +deletion: + +``` +# client +client_send_action_to_server({"action": "delete_prop", "type": type_name, "uuid": uuid}) + +# server, in server_action_received(data) +"delete_prop": + var prop := _find_prop_by_uuid(str(data.get("uuid", ""))) + if prop != null: + prop.queue_free() # held locally -> _exit_tree replicates the delete + else: + # Not held by THIS server (e.g. the prop was loaded from the database on another + # instance): emit the delete message to Horizon directly so it leaves the GORC and + # the database anyway. + _on_prop_delete(str(data.get("uuid", "")), str(data.get("type", ""))) +``` + +:::note[The delete message vs the trigger] +What actually deletes the object is the `props/delete_object` message sent to Horizon +(`{namespace: "props", event: "delete_object", data: [{uuid, type}]}`). Freeing the node via +*_exit_tree* is just the **usual trigger**; a server can also send that message directly for +a prop it does not currently hold as a node. +::: + +:::warning[Database deletion requires a bridge subscription] +For the deletion to reach the database, the `ds_bridge` persistence service must be +subscribed to `plugin:genericprops:delete_object` in `plugins.toml`. The Docker +configuration (`.docker/plugins.toml`) already is; a bare-metal `plugins.toml` may not be. +:::