references/migration-notes.md
# Migration notes: godot-server-architecture
Incremental upgrade for topics this skill covers. Apply **one hop**, stabilize/test, then next. Never skip hops.
If the project is **< 4.0**, follow [godot-version-migration](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-version-migration/SKILL.md) era bridges (legacy → 3→4) until 4.0, then these hops. Official 3→4: [Upgrading from Godot 3 to Godot 4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.html).
## 4.0 → 4.1
Official: [Upgrading to Godot 4.1](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.1.html)
- `WebRTCPeerConnectionExtension._create_data_channel()` return type is `WebRTCDataChannel`.
## 4.1 → 4.2
Official: [Upgrading to Godot 4.2](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.2.html)
*No skill-relevant breaking changes for this hop.*
## 4.2 → 4.3
Official: [Upgrading to Godot 4.3](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.3.html)
- `SceneMultiplayer` protocol break — dedicated server and clients must share the same minor version.
## 4.3 → 4.4
Official: [Upgrading to Godot 4.4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.4.html)
*No skill-relevant breaking changes for this hop.*
## 4.4 → 4.5
Official: [Upgrading to Godot 4.5](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.5.html)
- `Node.get_rpc_config()` → **`get_node_rpc_config()`**.
- `JSONRPC.set_scope()` → **`set_method()`**.
- `Resource.duplicate(true)` deep-copies **internal** resources only — use `duplicate_deep(DEEP_DUPLICATE_ALL)` when cloning authoritative server state graphs.
## 4.5 → 4.6
Official: [Upgrading to Godot 4.6](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.6.html)
- `StreamPeerTCP` → **`StreamPeerSocket`**; migrate custom TCP gate/login servers.
## 4.6 → 4.7
Official: [Upgrading to Godot 4.7](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.7.html)
- Packed array element assignment no longer triggers whole-property setter — audit replicated state bags using typed arrays.
- Typed-return overrides must explicitly `return` — fix headless server GDScript that relied on implicit null.
references/rendering-physics-server-cookbook.md
# RenderingServer / PhysicsServer cookbook
Use when SceneTree nodes cannot meet tick budget — pair with [physics_server_direct.gd](../scripts/physics_server_direct.gd).
## RenderingServer canvas item (2D)
```gdscript
var canvas_item := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(canvas_item, get_canvas_item())
var texture_rid := load("res://icon.png").get_rid()
RenderingServer.canvas_item_add_texture_rect(
canvas_item, Rect2(0, 0, 64, 64), texture_rid)
```
## PhysicsServer2D body
```gdscript
var body_rid := PhysicsServer2D.body_create()
PhysicsServer2D.body_set_mode(body_rid, PhysicsServer2D.BODY_MODE_RIGID)
var shape_rid := PhysicsServer2D.circle_shape_create()
PhysicsServer2D.shape_set_data(shape_rid, 16.0)
PhysicsServer2D.body_add_shape(body_rid, shape_rid)
```
> [!CAUTION]
> Every `*_create()` needs matching `free_rid()` on dedicated hosts.
## When to use servers vs nodes
| Servers | Nodes |
|---------|-------|
| Procedural swarms, voxels, mass particles | Gameplay actors, UI, prototyping |
Interest management: `MultiplayerSynchronizer.public_visibility = false` + visibility filter — see Host Patterns in SKILL.md.
scripts/dtls_secure_server.gd
# dtls_secure_server.gd
# Encrypting ENet traffic using DTLS and certificates
extends Node
# EXPERT NOTE: DTLS provides encryption over UDP,
# preventing man-in-the-middle attacks on sensitive data.
func secure_server(crypto_key: CryptoKey, cert: X509Certificate):
var peer := ENetMultiplayerPeer.new()
peer.create_server(7000)
# Setting up the TLS/DTLS options for the host
var server_options := TLSOptions.server(crypto_key, cert)
peer.host.dtls_server_setup(server_options)
multiplayer.multiplayer_peer = peer
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_enetmultiplayerpeer.html
# - https://docs.godotengine.org/en/stable/classes/class_dtlsserver.html
# - https://docs.godotengine.org/en/stable/classes/class_tlsoptions.html
# - https://docs.godotengine.org/en/stable/classes/class_x509certificate.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/ssl_certificates.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — secure transport before RPC game traffic
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-export-builds/SKILL.md — ship cert/key assets with dedicated-server builds
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/enet_optimized_host.gd
# enet_optimized_host.gd
# Configuring high-performance UDP hosts for Godot servers
extends Node
# EXPERT NOTE: ENet is the preferred protocol for action games.
# Defining precise bandwidth and client limits is vital for stability.
func setup_enet_server(port: int, max_clients: int):
var peer := ENetMultiplayerPeer.new()
# Port, Max Clients, Channels (0 for default), In/Out Bandwidth (0 for unlimited)
var err := peer.create_server(port, max_clients, 0, 0, 0)
if err == OK:
multiplayer.multiplayer_peer = peer
print("Server listening on port ", port)
else:
push_error("ENet Server Setup Failed: ", err)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_enetmultiplayerpeer.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerapi.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — ENet channel/bandwidth tuning beyond create_server
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-adapt-single-to-multiplayer/SKILL.md — wire host peer into authority split
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/headless_init_manager.gd
# headless_init_manager.gd
# Detecting and initializing dedicated server environments
extends Node
# EXPERT NOTE: DisplayServer.get_name() returns "headless"
# only if the binary was launched with the --headless argument.
func _ready():
if DisplayServer.get_name() == "headless" or OS.has_feature("dedicated_server"):
print_rich("[color=green]DEDICATED SERVER DETECTED[/color]")
_start_server_logic()
func _start_server_logic():
# Configure server-specific singletons or physics speeds
Engine.max_fps = 60 # Servers don't need high FPS, but need stability
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html
# - https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_dedicated_servers.html
# - https://docs.godotengine.org/en/stable/classes/class_displayserver.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-export-builds/SKILL.md — dedicated-server export presets for headless hosts
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — start multiplayer peer after headless detect
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/headless_manager.gd
# skills/server-architecture/scripts/headless_manager.gd
extends Node
## Headless Server Manager Expert Pattern
## Manages headless state, arguments, and optimizations for dedicated servers.
class_name HeadlessManager
signal server_ready
signal server_shutdown
func _ready() -> void:
# 1. Detect Headless Mode
if DisplayServer.get_name() == "headless":
print("[HeadlessManager] Running in Headless Mode")
_configure_headless()
else:
print("[HeadlessManager] Running in Graphical Mode")
# 2. Parse Arguments
_parse_cmdline_args()
func _configure_headless() -> void:
# Disable visual-only processing if necessary
# Note: Godot 4 headless automatically disables rendering, but we can save more
# Limit physics if not needed, or lock FPS
Engine.max_fps = 60 # Server tick rate
# Lower audio bus volume or disable
AudioServer.set_bus_mute(0, true)
func _parse_cmdline_args() -> void:
var args = OS.get_cmdline_user_args()
for arg in args:
if arg.begins_with("--port="):
var port = arg.split("=")[1].to_int()
print("[HeadlessManager] Override Port: ", port)
# NetworkManager.start_server(port)
func _notification(what: int) -> void:
if what == NOTIFICATION_WM_CLOSE_REQUEST:
print("[HeadlessManager] Shutdown Requested")
server_shutdown.emit()
# Perform cleanup
# Save state
get_tree().quit()
## EXPERT USAGE:
## Add as AutoLoad. Call using standard --headless -- --port=7777
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html
# - https://docs.godotengine.org/en/stable/tutorials/export/feature_tags.html
# - https://docs.godotengine.org/en/stable/classes/class_os.html
# - https://docs.godotengine.org/en/stable/classes/class_displayserver.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-export-builds/SKILL.md — headless/dedicated export and CLI packaging
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-autoload-architecture/SKILL.md — Autoload host lifecycle for HeadlessManager
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/isolated_multiplayer_api.gd
# isolated_multiplayer_api.gd
# Running Client and Server instances in a single Godot run
extends Node
# EXPERT NOTE: Use for Local Hosting where the same instance
# needs to act as both authoritative server and local client.
func split_branches():
var server_api = MultiplayerAPI.create_default_interface()
# Isolate the /root/Server branch to its own MultiplayerAPI root
get_tree().set_multiplayer(server_api, ^"/root/Server")
print("Network branches isolated: Client and Server now run independently.")
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerapi.html
# - https://docs.godotengine.org/en/stable/classes/class_scenemultiplayer.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — SceneMultiplayer roots for host+client-in-one
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — /root/Server branch layout for isolated APIs
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/manual_network_poll.gd
# manual_network_poll.gd
# Running networking on a separate thread via manual polling
extends Node
# EXPERT NOTE: Disabling SceneTree.multiplayer_poll allows
# you to control exactly when network packets are processed.
func _ready():
# Stop the engine from automatically polling networking
get_tree().multiplayer_poll = false
func _physics_process(_delta):
# Manual pumping of the network stack, usually inside a Mutex lock
if multiplayer.has_multiplayer_peer():
multiplayer.poll()
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerapi.html
# - https://docs.godotengine.org/en/stable/classes/class_scenemultiplayer.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — poll timing with transfer modes and sync Hz
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — off-main-thread poll vs frame budget
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/peer_kick_manager.gd
# peer_kick_manager.gd
# Gracefully terminating peer connections
extends Node
# EXPERT NOTE: Disconnecting peers forcefully (disconnect_peer)
# is cleaner than just erasing them from a list.
func remove_player(peer_id: int, reason: String):
# Notify the peer first if possible
_on_kicked.rpc_id(peer_id, reason)
# Drop connection
multiplayer.disconnect_peer(peer_id)
print("Kicked peer ", peer_id, " for: ", reason)
@rpc("authority", "call_remote", "reliable")
func _on_kicked(reason: String):
print("Disconnected by server: ", reason)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerapi.html
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerpeer.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — disconnect/kick with lobby state cleanup
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — local kicked/disconnected events off transport
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/physics_server_direct.gd
# physics_server_direct.gd
# Bypassing SceneTree overhead for high-density simulations
extends Node3D
# EXPERT NOTE: For MMO-scale logic, Nodes are too expensive.
# Create bodies directly on the PhysicsServer3D and manage RIDs.
var server_bodies: Array[RID] = []
func spawn_server_body(xform: Transform3D) -> RID:
var body_rid := PhysicsServer3D.body_create()
PhysicsServer3D.body_set_mode(body_rid, PhysicsServer3D.BODY_MODE_KINEMATIC)
# Link to the 3D world's physics space
PhysicsServer3D.body_set_space(body_rid, get_world_3d().space)
PhysicsServer3D.body_set_state(body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, xform)
server_bodies.append(body_rid)
return body_rid
func _exit_tree():
for rid in server_bodies:
PhysicsServer3D.free_rid(rid)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/performance/using_servers.html
# - https://docs.godotengine.org/en/stable/classes/class_physicsserver3d.html
# - https://docs.godotengine.org/en/stable/classes/class_rid.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-physics-3d/SKILL.md — node physics baseline before RID body_create
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — when SceneTree bodies become too expensive
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/rid_performance_server.gd
# skills/server-architecture/code/rid_performance_server.gd
extends Node
## Server Architecture Expert Pattern
## Implements High-Performance RID Management (Scene Tree Bypass).
var _instance_rids: Array[RID] = []
var _mesh_rid: RID
var _material_rid: RID
func _enter_tree() -> void:
# 1. Resource ID (RID) Mastery
# Expert logic: Manually manage drawing without MeshInstance3D nodes.
_mesh_rid = RenderingServer.mesh_create()
# Assume a pre-loaded mesh resource for brevity
# RenderingServer.mesh_add_surface_from_arrays(_mesh_rid, RenderingServer.PRIMITIVE_TRIANGLES, arrays)
_material_rid = RenderingServer.material_create()
func spawn_instances(count: int, area_size: float) -> void:
# 2. Direct RenderingServer Calls
# This bypasses the overhead of 10,000 Node3D objects.
for i in range(count):
var instance = RenderingServer.instance_create()
RenderingServer.instance_set_base(instance, _mesh_rid)
RenderingServer.instance_set_scenario(instance, get_world_3d().scenario)
var xform = Transform3D(Basis(), Vector3(
randf_range(-area_size, area_size),
0,
randf_range(-area_size, area_size)
))
RenderingServer.instance_set_transform(instance, xform)
_instance_rids.append(instance)
func query_physics_direct(origin: Vector3, direction: Vector3) -> Dictionary:
# 3. Direct PhysicsServer Queries
# Professional pattern: Query the server directly instead of using RayCast3D node.
var space_state = PhysicsServer3D.space_get_direct_state(get_world_3d().space)
var query = PhysicsRayQueryParameters3D.create(origin, origin + direction * 100.0)
return space_state.intersect_ray(query)
func _exit_tree() -> void:
# CRITICAL: Manual cleanup of RIDs is mandatory to prevent memory leaks.
for rid in _instance_rids:
RenderingServer.free_rid(rid)
RenderingServer.free_rid(_mesh_rid)
RenderingServer.free_rid(_material_rid)
## EXPERT NOTE:
## Use 'WorkerThreadPool Batching': For 1 million+ calculations, split
## the loop across cores: 'WorkerThreadPool.add_native_group_task(self, "_proc", count)'.
## For 'Headless Simulation', run Godot with '--headless' to disable
## the OS window and Vulkan/OpenGL context for pure low-latency servers.
## NEVER instantiate Nodes for pure data or invisible calculation;
## use RIDs or plain Objects to save 90% memory overhead.
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/performance/using_servers.html
# - https://docs.godotengine.org/en/stable/classes/class_renderingserver.html
# - https://docs.godotengine.org/en/stable/classes/class_physicsserver3d.html
# - https://docs.godotengine.org/en/stable/classes/class_rid.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — RID instance pools vs MultiMesh/node budgets
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-procedural-generation/SKILL.md — consumer of mass RID spawn/despawn
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/safe_packet_decoder.gd
# safe_packet_decoder.gd
# Preventing RCE vulnerabilities in network serialization
extends Node
# EXPERT NOTE: NEVER pass true to get_var/set_var on untrusted data.
# Object decoding allows a client to trigger arbitrary code.
func process_untrusted_packet(packet_peer: PacketPeerUDP):
if packet_peer.get_available_packet_count() > 0:
# EXPERT: Passing 'false' forbids Object decoding, preventing RCE.
var data: Variant = packet_peer.get_var(false)
_handle_data(data)
func _handle_data(data: Variant): pass
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/io/binary_serialization_api.html
# - https://docs.godotengine.org/en/stable/classes/class_packetpeer.html
# - https://docs.godotengine.org/en/stable/classes/class_packetpeerudp.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — apply same no-object-decode rule to custom RPCs
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-adapt-single-to-multiplayer/SKILL.md — validate untrusted client payloads at authority
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/server_authority_validator.gd
# server_authority_validator.gd
# Validating client requests at the entry point
extends Node
# EXPERT NOTE: RPC authority checks are the first line of defense.
# Use get_remote_sender_id() to identify and validate peers.
@rpc("any_peer", "call_local", "reliable")
func commit_transaction(item_id: String, amount: int):
if not multiplayer.is_server(): return
var peer_id = multiplayer.get_remote_sender_id()
if _can_afford(peer_id, amount):
_apply_transaction(peer_id, item_id, amount)
else:
_notify_error.rpc_id(peer_id, "Insufficient funds")
@rpc("authority", "call_remote", "reliable")
func _notify_error(msg: String): pass
func _can_afford(id, amt): return true
func _apply_transaction(id, item, amt): pass
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# - https://docs.godotengine.org/en/stable/classes/class_multiplayerapi.html
# - https://docs.godotengine.org/en/stable/classes/class_node.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — authority/RPC patterns for validated actions
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-monte-carlo-balancer/SKILL.md — economy checks after authority rules change TTK
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/server_health_exporter.gd
class_name ServerHealthExporter
extends Node
## Exports server telemetry and performance metrics for external monitoring (Prometheus/Grafana).
## Runs automatically in headless/dedicated server mode.
@export var export_interval: float = 10.0 # Seconds
func _ready() -> void:
# Only run on dedicated/headless servers to save client resources
if DisplayServer.get_name() != "headless":
queue_free()
return
var timer = Timer.new()
timer.wait_time = export_interval
timer.autostart = true
timer.timeout.connect(_export_metrics)
add_child(timer)
func _export_metrics() -> void:
var metrics = {
"timestamp": Time.get_unix_time_from_system(),
"performance": {
"fps": Performance.get_monitor(Performance.TIME_FPS),
"process": Performance.get_monitor(Performance.TIME_PROCESS),
"physics_process": Performance.get_monitor(Performance.TIME_PHYSICS_PROCESS),
"static_memory": Performance.get_monitor(Performance.MEMORY_STATIC),
"objects": Performance.get_monitor(Performance.OBJECT_COUNT),
"nodes": Performance.get_monitor(Performance.OBJECT_NODE_COUNT),
"orphan_nodes": Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT)
},
"network": {
"peers": multiplayer.get_peers().size(),
"bandwidth_in": _get_enet_bandwidth_in(),
"bandwidth_out": _get_enet_bandwidth_out()
}
}
# Print to standard output in JSON format for scraping tools like Filebeat or Promtail
print("METRICS_DUMP:" + JSON.stringify(metrics))
func _get_enet_bandwidth_in() -> float:
var peer = multiplayer.multiplayer_peer
if peer is ENetMultiplayerPeer:
# Note: ENet doesn't expose raw bandwidth easily in high-level API,
# but you can track it via custom packet counting.
return 0.0
return 0.0
func _get_enet_bandwidth_out() -> float:
return 0.0
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_performance.html
# - https://docs.godotengine.org/en/stable/classes/class_displayserver.html
# - https://docs.godotengine.org/en/stable/classes/class_enetmultiplayerpeer.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md — Performance monitors and headless telemetry habits
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md — act on FPS/memory/orphan signals from exporters
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/server_matchmaker_client.gd
class_name ServerMatchmakerClient
extends Node
## Client-side logic for connecting to a central Load Balancer/Matchmaker.
## Uses HTTP to receive a dedicated server IP/Port handoff.
@export var matchmaker_url: String = "https://api.game.com/v1/match"
@export var auth_token: String = ""
signal match_found(ip: String, port: int)
signal match_failed(reason: String)
var _http: HTTPRequest
func _ready() -> void:
_http = HTTPRequest.new()
add_child(_http)
_http.request_completed.connect(_on_request_completed)
## Requests a server assignment from the Load Balancer.
func request_match(region: String = "us-east") -> void:
var headers = ["Content-Type: application/json"]
if not auth_token.is_empty():
headers.append("Authorization: Bearer " + auth_token)
var body = JSON.stringify({"region": region})
_http.request(matchmaker_url, headers, HTTPClient.METHOD_POST, body)
func _on_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
match_failed.emit("HTTP Error: %d" % response_code)
return
var json = JSON.new()
var err = json.parse(body.get_string_from_utf8())
if err != OK:
match_failed.emit("JSON Parse Error")
return
var data = json.get_data()
if data.has("ip") and data.has("port"):
match_found.emit(data.ip, int(data.port))
else:
match_failed.emit("Malformed matchmaker response")
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/networking/http_client_class.html
# - https://docs.godotengine.org/en/stable/classes/class_httprequest.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/ssl_certificates.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — connect ENet/WebSocket after matchmaker handoff
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-platform-web/SKILL.md — HTTPS matchmaker from browser builds
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
scripts/websocket_server_compat.gd
# websocket_server_compat.gd
# WebSocket implementation for HTML5/Web browser servers
extends Node
# EXPERT NOTE: ENet is UDP-only and unsupported in browsers.
# WebSocketMultiplayerPeer is required for web compatibility.
func start_web_server(port: int):
var peer := WebSocketMultiplayerPeer.new()
var err = peer.create_server(port)
if err == OK:
multiplayer.multiplayer_peer = peer
print("WebSocket Server active on port ", port)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html
# - https://docs.godotengine.org/en/stable/classes/class_websocketmultiplayerpeer.html
# - https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-platform-web/SKILL.md — HTML5 client constraints requiring WebSocket peers
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md — shared high-level API over WebSocket transport
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-server-architecture/SKILL.md
# =============================================================================
SKILL.md
---
name: godot-server-architecture
description: "Expert blueprint for dedicated / headless multiplayer hosts: ENet/DTLS, authority validation, safe packet decode, matchmaker handoff, and health telemetry. Use when building authoritative servers, --headless hosts, or hardening host networking. Keywords: dedicated server, headless, ENet, DTLS, authority, safe_packet_decoder, multiplayer host, WebSocketMultiplayerPeer."
---
## Skill boundary (Do NOT Load)
| Use **this skill** for | Use **godot-multiplayer-networking** for |
| :--- | :--- |
| `--headless` / dedicated export boot | Lobby UI, matchmaking UX, friend invites |
| ENet/DTLS host peer + safe decode | RPC signatures, `@rpc` gameplay handlers |
| Authority validation on privileged ops | MultiplayerSynchronizer / scene replication |
| Kick, health telemetry, matchmaker handoff | Client prediction, lag compensation |
**Do NOT Load** lobby/RPC tutorial scripts from multiplayer-networking when only booting a host — follow Host Golden Path here first.
## Host Golden Path (MANDATORY)
1. **Headless detect/init** — **MANDATORY** [headless_init_manager.gd](scripts/headless_init_manager.gd) (`--headless` / `dedicated_server` feature).
2. **Safe decode** — **MANDATORY** [safe_packet_decoder.gd](scripts/safe_packet_decoder.gd) before any untrusted `get_var`.
3. **Host peer** — [enet_optimized_host.gd](scripts/enet_optimized_host.gd); add [dtls_secure_server.gd](scripts/dtls_secure_server.gd) when encrypting UDP.
4. **Authority** — [server_authority_validator.gd](scripts/server_authority_validator.gd) on every privileged RPC.
5. **Ops** — [peer_kick_manager.gd](scripts/peer_kick_manager.gd), [server_health_exporter.gd](scripts/server_health_exporter.gd); matchmaker handoff via [server_matchmaker_client.gd](scripts/server_matchmaker_client.gd).
## Available Scripts
### [headless_init_manager.gd](scripts/headless_init_manager.gd)
Detect/initialize dedicated server logic for `--headless` / `dedicated_server`.
### [headless_manager.gd](scripts/headless_manager.gd)
Headless runtime manager companion patterns.
### [enet_optimized_host.gd](scripts/enet_optimized_host.gd)
High-performance ENet UDP hosts with bandwidth/client limits.
### [dtls_secure_server.gd](scripts/dtls_secure_server.gd)
DTLS + X509 hardening for ENet UDP.
### [safe_packet_decoder.gd](scripts/safe_packet_decoder.gd)
Forbid object decoding on untrusted packets (RCE guard).
### [manual_network_poll.gd](scripts/manual_network_poll.gd)
Manual `multiplayer.poll()` when auto-poll is disabled.
### [isolated_multiplayer_api.gd](scripts/isolated_multiplayer_api.gd)
Isolated MultiplayerAPI instances (client+server in one process).
### [server_authority_validator.gd](scripts/server_authority_validator.gd)
`get_remote_sender_id()` gates for authoritative requests.
### [websocket_server_compat.gd](scripts/websocket_server_compat.gd)
HTML5-compatible `WebSocketMultiplayerPeer` hosts.
### [peer_kick_manager.gd](scripts/peer_kick_manager.gd)
Graceful peer termination with reason propagation.
### [server_matchmaker_client.gd](scripts/server_matchmaker_client.gd)
Load-balancer / matchmaker handoff to game hosts.
### [server_health_exporter.gd](scripts/server_health_exporter.gd)
Headless telemetry for monitoring stacks.
### [physics_server_direct.gd](scripts/physics_server_direct.gd) / [rid_performance_server.gd](scripts/rid_performance_server.gd)
Optional host-side RID sim — **only when** node physics cannot hold tick budget: > ~200 active bodies per tick, or headless host CPU > 70% on physics step with nodes. Criteria: profile first; if SceneTree bodies dominate, prefer [godot-performance-optimization](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md). **Do NOT Load** RID scripts for ≤64 entity lobbies.
## NEVER Do in Server Architecture (Host)
- **NEVER trust the client** — Validate state, purchases, and damage on the authoritative host.
- **NEVER use `TRANSFER_MODE_RELIABLE` for continuous streams** — Prefer unreliable for high-rate transforms.
- **NEVER use `get_var(true)` on untrusted packets** — Object decode = RCE. **MANDATORY** safe_packet_decoder.
- **NEVER use TCP for fast-paced action** — Prefer ENet UDP (or WebSocket for HTML5 constraints).
- **NEVER run a dedicated server without stripping visuals** — Dedicated Server export / dummy drivers.
- **NEVER expect RPCs before `connected_to_server` / peer ready**.
- **NEVER assume `UNRELIABLE` packets arrive in order**.
- **NEVER leave `SceneTree.multiplayer_poll` false without manual `poll()`**.
- **NEVER mix incompatible engine/multiplayer protocol versions across peers**.
- **NEVER forget `free_rid` on server-created RIDs** if the host uses Physics/RenderingServer pools.
## Host Patterns
### Interest management
Large worlds: `MultiplayerSynchronizer.public_visibility = false` + visibility filters (AABB / grid) so the host does not sync the entire world to every peer.
```gdscript
# Hook on synchronizer — filter peers by grid cell / AABB (no full tutorial)
func _visibility_filter(for_peer: int, node: Node) -> bool:
return _interest_grid.is_visible_to_peer(for_peer, node.global_position)
# Assign: synchronizer.set_visibility_filter(_visibility_filter)
```
### Health metrics
Watch host FPS, static memory (RID leaks), and orphan counts via [server_health_exporter.gd](scripts/server_health_exporter.gd).
## Deep recipes (on demand)
> LLM-ignorance rule: if a general agent would not know it before reading, it lives here or in `scripts/` — never delete, only move.
| Topic | Reference |
|-------|-----------|
| RID canvas/physics cookbook | [rendering-physics-server-cookbook.md](references/rendering-physics-server-cookbook.md) |
## Reference
> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
### Official Documentation
- [Using Servers](https://docs.godotengine.org/en/stable/tutorials/performance/using_servers.html) — RID-based RenderingServer/PhysicsServer/NavigationServer workflow when SceneTree nodes are too slow.
- [RenderingServer](https://docs.godotengine.org/en/stable/classes/class_renderingserver.html) — `canvas_item_*` / `instance_*` / `free_rid` for procedural draw and mesh swarms without MeshInstance nodes.
- [PhysicsServer3D](https://docs.godotengine.org/en/stable/classes/class_physicsserver3d.html) — `body_create`, space binding, and direct-state queries for headless authoritative simulation.
- [PhysicsServer2D](https://docs.godotengine.org/en/stable/classes/class_physicsserver2d.html) — 2D body/shape RIDs mirroring the same SceneTree-bypass pattern.
- [RID](https://docs.godotengine.org/en/stable/classes/class_rid.html) — opaque server handles; every `*_create()` needs a matching `free_rid` to avoid leaks.
- [High-level multiplayer](https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html) — authority, RPCs, and peer lifecycle for dedicated hosts and isolated MultiplayerAPI branches.
- [ENetMultiplayerPeer](https://docs.godotengine.org/en/stable/classes/class_enetmultiplayerpeer.html) — UDP host creation, channels/bandwidth limits, and DTLS host setup on `peer.host`.
- [WebSocket multiplayer](https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html) — browser-compatible peer path when ENet UDP is unavailable (HTML5 clients).
- [Exporting for dedicated servers](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_dedicated_servers.html) — dedicated-server export presets and stripping visuals/audio for production hosts.
- [Command line tutorial](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html) — `--headless` and CLI flags used by headless init/managers.
- [Binary serialization API](https://docs.godotengine.org/en/stable/tutorials/io/binary_serialization_api.html) — `get_var(false)` / object-decoding rules that block RCE on untrusted packets.
- [DTLSServer](https://docs.godotengine.org/en/stable/classes/class_dtlsserver.html) — DTLS accept path complementary to ENet `dtls_server_setup` with X509/TLSOptions.
### Related Skills
#### Prerequisites
- [godot-project-foundations](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md) — project layout, Autoloads, and feature tags that dedicated-server and headless launches depend on.
- [godot-gdscript-mastery](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md) — typed RID arrays, `@rpc` annotations, and safe Variant decoding patterns used across server scripts.
- [godot-physics-3d](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-physics-3d/SKILL.md) — node-level PhysicsBody3D/space concepts before bypassing them with PhysicsServer3D RIDs.
#### Complements
- [godot-multiplayer-networking](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md) — lobby/RPC/synchronizer toolkit that sits on the headless ENet/WebSocket hosts this skill scaffolds.
- [godot-adapt-single-to-multiplayer](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-adapt-single-to-multiplayer/SKILL.md) — authority split and prediction shells before wiring dedicated-server validation and interest filters.
- [godot-export-builds](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-export-builds/SKILL.md) — dedicated-server export presets and CLI packaging for real multi-instance host tests.
- [godot-2d-physics](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-2d-physics/SKILL.md) — PhysicsServer2D body/shape patterns for 2D authoritative swarms without SceneTree bodies.
- [godot-navigation-pathfinding](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-navigation-pathfinding/SKILL.md) — NavigationServer RIDs and bake updates when AI agents share the same low-level server path.
- [godot-performance-optimization](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-performance-optimization/SKILL.md) — budgets and profiling that decide when RID servers beat nodes under peer/object load.
- [godot-debugging-profiling](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-debugging-profiling/SKILL.md) — Performance monitors and remote debug habits for headless FPS/memory/orphan telemetry.
- [godot-platform-web](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-platform-web/SKILL.md) — HTML5 client constraints that force WebSocketMultiplayerPeer instead of ENet.
#### Downstream / consumers
- [godot-procedural-generation](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-procedural-generation/SKILL.md) — mass object/voxel spawners that consume RenderingServer/PhysicsServer RID pools.
- [godot-monte-carlo-balancer](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-monte-carlo-balancer/SKILL.md) — retune economy/TTK after authoritative server tick rates or validation change effective combat windows.
- [godot-genre-battle-royale](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-genre-battle-royale/SKILL.md) — large-peer dedicated hosts that need interest grids, kick/health exporters, and RID-scale sim.
#### Master
- [godot-master](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-master/SKILL.md) — library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting server concern.