
Godot Server Architecture
- 160 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-server-architecture for development tasks
About
godot-server-architecture: A skill for development. This provides functionality for development workflows.
- godot-server-architecture
Godot Server Architecture by the numbers
- 160 all-time installs (skills.sh)
- +18 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,394 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-server-architectureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 160 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-server-architecture for development tasks
Files
Server Architecture
RID-based server API, direct rendering/physics access, and object pooling define maximum-performance patterns.
Available Scripts
headless_init_manager.gd
Automatically detecting and initializing dedicated server logic when launched with --headless or dedicated_server features.
enet_optimized_host.gd
Expert initialization of high-performance ENet UDP hosts with precise bandwidth and client limits.
dtls_secure_server.gd
Securing ENet UDP traffic using DTLS and X509 certificates to prevent man-in-the-middle attacks.
physics_server_direct.gd
Massive scale simulation pattern that bypasses the SceneTree by creating bodies directly on the PhysicsServer3D.
safe_packet_decoder.gd
Crucial network security pattern that explicitly forbids object decoding to prevent Remote Code Execution (RCE) vulnerabilities.
manual_network_poll.gd
Moving networking off the main thread by disabling automatic polling and managing manual multiplayer.poll() loops.
isolated_multiplayer_api.gd
Pattern for running Client and Server branches independently within a single Godot instance via isolated API instances.
server_authority_validator.gd
Authoritative entry point validation using get_remote_sender_id() to strictly verify client requests.
websocket_server_compat.gd
Ensuring compatibility with HTML5/Web browser clients using WebSocketMultiplayerPeer architecture.
peer_kick_manager.gd
Graceful termination and cleanup of peer connections with custom reason propagation.
server_matchmaker_client.gd
Client-side handoff logic for connecting to servers via a Load Balancer/Matchmaker.
server_health_exporter.gd
Telemetry exporter for headless servers to monitoring stacks (Prometheus/Grafana).
NEVER Do in Server Architecture
- NEVER trust the client — Validate all state changes, purchases, and damage exclusively on the authoritative server to prevent cheating [28].
- NEVER use `TRANSFER_MODE_RELIABLE` for continuous data streams — Synchronizing coordinates every frame using reliable mode causes extreme network congestion; always use
UNRELIABLE[29]. - NEVER use `get_var(true)` on untrusted network packets — Passing
trueallows the engine to deserialize arbitrary objects, creating a critical Remote Code Execution vulnerability [30]. - NEVER use TCP for fast-paced action games — TCP's Nagle's algorithm and congestion control cause unacceptable latency; use Godot's built-in ENet (UDP) [31].
- NEVER run a dedicated server without stripping visuals — Always export using "Dedicated Server" mode or use the
Dummyaudio/physics drivers to prevent GPU/CPU waste [32]. - NEVER expect RPCs to work before connection — Calling an RPC on a client before the
connected_to_serversignal has fired will fail [34]. - NEVER assume `UNRELIABLE` packets arrive in order — UDP packets can arrive out of order or be dropped; design state interpolation to handle missing ticks [31].
- NEVER leave `SceneTree.multiplayer_poll` set to false without manually calling `poll()` — Disabling auto-polling without manual polling freezes all network traffic [35].
- NEVER attempt to connect Godot clients and servers running different engine versions — The high-level multiplayer API protocol is version-specific and breaking [36].
- NEVER forget to unbind or free RIDs —
PhysicsServer3D.body_create()withoutfree_rid()causes massive server-side memory leaks over time.
---
Direct access to rendering without nodes.
# Create canvas item (2D sprite equivalent)
var canvas_item := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(canvas_item, get_canvas_item())
# Draw texture
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
Create physics bodies without nodes.
# Create body
var body_rid := PhysicsServer2D.body_create()
PhysicsServer2D.body_set_mode(body_rid, PhysicsServer2D.BODY_MODE_RIGID)
# Create shape
var shape_rid := PhysicsServer2D.circle_shape_create()
PhysicsServer2D.shape_set_data(shape_rid, 16.0) # radius
# Assign shape to body
PhysicsServer2D.body_add_shape(body_rid, shape_rid)When to Use Servers
Use servers for:
- Procedural generation (thousands of objects)
- Particle systems
- Voxel engines
- Custom rendering
Use nodes for:
- Regular game objects
- UI
- Prototyping
Advanced Server Patterns
1. Grid-Based Interest Management
In large worlds, don't sync everything. Use AABB checks on the server to only sync objects within a player's immediate "interest grid".
- Logic: Set
MultiplayerSynchronizer.public_visibility = false. - Filter:
add_visibility_filter(func(id): return player_aabb.has_point(object_pos)).
2. Server Health Metrics
Monitoring dedicated servers is vital for scaling.
- FPS: Drop below 60/30 indicates simulation lag.
- Memory: Static memory growth indicates RID leaks.
- Nodes: High orphan counts indicate incorrect cleanup.
Reference
Related
- Master Skill: godot-master
# 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
# 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)
# 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
# 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
# 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.")
# 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()
# 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)
# 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)
# 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.
# 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
# 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
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
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")
# 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)