godot-multiplayer-networking
0
总安装量
3
周安装量
安装命令
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-multiplayer-networking
Agent 安装分布
opencode
3
gemini-cli
3
codex
3
claude-code
2
github-copilot
2
Skill 文档
Multiplayer Networking
Authoritative servers, client prediction, and state synchronization define robust multiplayer.
Available Scripts
server_authoritative_controller.gd
Advanced player controller with client prediction, server reconciliation, and interpolation.
client_prediction_synchronizer.gd
Expert client-side prediction with server reconciliation pattern.
NEVER Do in Multiplayer Networking
- NEVER trust client input without server validation â Client sends “deal 9999 damage” RPC? Cheating. Server MUST validate actions:
if not multiplayer.is_server(): return. - NEVER use
@rpc("any_peer")for gameplay actions â Any peer can call = cheating vector. Use@rpc("authority")for damage/spawns. Onlyany_peerfor chat/cosmetics. - NEVER use reliable RPCs for position updates â 60 position updates/sec with
"reliable"= bandwidth explosion + lag. Use"unreliable"for frequent, non-critical data. - NEVER forget to set multiplayer authority â Both client and server process input? Desync. Call
set_multiplayer_authority(peer_id)and checkis_multiplayer_authority(). - NEVER sync every variable â MultiplayerSynchronizer syncing 50 properties = bandwidth waste. Only sync state OTHER clients need (skip animation frame, local UI state).
- NEVER block on
peer.create_server()â ENet is async. Callingmultiplayer.multiplayer_peer = peerbefore server ready = crash. Awaitpeer_connectedsignal. - NEVER forget interpolation for remote players â Teleporting remote players (direct position assignment) = jittery. Use
lerp()to smooth between received positions.
Authoritative Server Model:
- Server validates all game state
- Clients send inputs, receive state
- Prevents cheating
Peer-to-Peer:
- Direct player connections
- Good for small player counts
- No dedicated server needed
Basic Setup
Create Multiplayer Peer
extends Node
var peer := ENetMultiplayerPeer.new()
func host_game(port: int = 7777) -> void:
peer.create_server(port, 4) # Max 4 players
multiplayer.multiplayer_peer = peer
print("Server started on port ", port)
func join_game(ip: String, port: int = 7777) -> void:
peer.create_client(ip, port)
multiplayer.multiplayer_peer = peer
print("Connecting to ", ip)
Connection Signals
func _ready() -> void:
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
multiplayer.connected_to_server.connect(_on_connected)
multiplayer.connection_failed.connect(_on_connection_failed)
func _on_peer_connected(id: int) -> void:
print("Player connected: ", id)
func _on_peer_disconnected(id: int) -> void:
print("Player disconnected: ", id)
func _on_connected() -> void:
print("Connected to server!")
func _on_connection_failed() -> void:
print("Connection failed")
Remote Procedure Calls (RPCs)
Basic RPC
extends CharacterBody2D
# Called on all peers
@rpc("any_peer", "call_local")
func take_damage(amount: int) -> void:
health -= amount
if health <= 0:
die()
# Usage: Call on specific peer
take_damage.rpc_id(1, 50) # Call on server (ID 1)
take_damage.rpc(50) # Call on all peers
RPC Modes
# Only server can call, runs on all clients
@rpc("authority", "call_remote")
func server_spawn_enemy(pos: Vector2) -> void:
pass
# Any peer can call, runs locally too
@rpc("any_peer", "call_local")
func player_chat(message: String) -> void:
pass
# Reliable (TCP-like) vs Unreliable (UDP-like)
@rpc("any_peer", "call_local", "reliable")
func important_event() -> void:
pass
@rpc("any_peer", "call_local", "unreliable")
func position_update(pos: Vector2) -> void:
pass
MultiplayerSpawner
# Add MultiplayerSpawner node
# Set spawn path and scenes
extends Node
@onready var spawner := $MultiplayerSpawner
func _ready() -> void:
spawner.spawn_function = spawn_player
func spawn_player(data: Variant) -> Node:
var player := preload("res://player.tscn").instantiate()
player.name = str(data) # Use peer ID as name
return player
MultiplayerSynchronizer
# Add to synchronized node
# Set properties to sync
# Scene structure:
# Player (CharacterBody2D)
# ââ MultiplayerSynchronizer
# â ââ Replication config:
# â - position (sync)
# â - velocity (sync)
# â - health (sync)
# ââ Sprite2D
Lobby System
# lobby_manager.gd (AutoLoad)
extends Node
signal player_joined(id: int, info: Dictionary)
signal player_left(id: int)
var players: Dictionary = {}
func _ready() -> void:
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
func _on_peer_connected(id: int) -> void:
# Request player info
request_player_info.rpc_id(id)
func _on_peer_disconnected(id: int) -> void:
players.erase(id)
player_left.emit(id)
@rpc("any_peer", "reliable")
func request_player_info() -> void:
var sender_id := multiplayer.get_remote_sender_id()
receive_player_info.rpc_id(sender_id, {
"name": PlayerSettings.player_name,
"color": PlayerSettings.player_color
})
@rpc("any_peer", "reliable")
func receive_player_info(info: Dictionary) -> void:
var sender_id := multiplayer.get_remote_sender_id()
players[sender_id] = info
player_joined.emit(sender_id, info)
State Synchronization
extends CharacterBody2D
var puppet_position: Vector2
var puppet_velocity: Vector2
func _physics_process(delta: float) -> void:
if is_multiplayer_authority():
# Local player: process input
_handle_input(delta)
move_and_slide()
# Send position to others
sync_position.rpc(global_position, velocity)
else:
# Remote player: interpolate
global_position = global_position.lerp(puppet_position, 10.0 * delta)
@rpc("any_peer", "unreliable")
func sync_position(pos: Vector2, vel: Vector2) -> void:
puppet_position = pos
puppet_velocity = vel
Authority
# Check who owns this node
func _ready() -> void:
# Set authority to owner peer
set_multiplayer_authority(peer_id)
func _process(delta: float) -> void:
if not is_multiplayer_authority():
return # Skip if not owner
# Only authority processes this
Best Practices
1. Validate on Server
@rpc("any_peer", "call_local")
func player_action(action: String) -> void:
if not multiplayer.is_server():
return # Only server validates
var sender := multiplayer.get_remote_sender_id()
if not _is_valid_action(sender, action):
return
_apply_action.rpc(sender, action)
2. Use Unreliable for Frequent Updates
# Position: unreliable (frequent)
@rpc("any_peer", "unreliable")
func sync_position(pos: Vector2) -> void:
pass
# Damage: reliable (important)
@rpc("authority", "reliable")
func apply_damage(amount: int) -> void:
pass
3. Interpolation for Smooth Movement
var target_position: Vector2
func _process(delta: float) -> void:
if not is_multiplayer_authority():
position = position.lerp(target_position, 15.0 * delta)
Reference
Related
- Master Skill: godot-master