← BACK TO HOME
NETWORKINGIntermediate⏱ 15 min

P2P Multiplayer in Godot 4

Godot 4's high-level multiplayer API makes peer-to-peer sessions surprisingly approachable — if you know which pitfalls to dodge. This lesson walks you through establishing a WebRTC or ENet connection, synchronising player state with MultiplayerSynchronizer, and applying rollback-style prediction for low-latency fighting-game inputs. You'll leave with a working 2-player lobby you can extend into a full game.

WHAT YOU'LL LEARN

  • ▸Configure ENet or WebRTC peers and handle connection/disconnection events cleanly
  • ▸Use MultiplayerSynchronizer to sync position, velocity, and custom state properties
  • ▸Implement input prediction and server reconciliation for a snappy feel even at 150ms ping
  • ▸Separate authoritative server logic from client-side cosmetic interpolation
  • ▸Handle host migration so a session survives when the original host leaves

INSTRUCTOR

T
Tomas Vrábel
@tvr_gamedev
SHIPPED:
Fracture Protocol (itch.io, 2023)

Tomas built Fracture Protocol — a fast-paced 4-player tactical shooter — in Godot 4 with full P2P networking. He's been shipping multiplayer games for seven years and knows every edge case Godot's netcode hides.

GDScript · lesson snippet
LOCKED
# lobby.gd  (autoload)
extends Node

const PORT      = 7777
const MAX_PEERS = 8

signal player_connected(peer_id: int)
signal player_disconnected(peer_id: int)
signal connection_failed

func host_game() -> void:
    var peer = ENetMultiplayerPeer.new()
    peer.create_server(PORT, MAX_PEERS)
    multiplayer.multiplayer_peer = peer
    multiplayer.peer_connected.connect(_on_peer_connected)
    multiplayer.peer_disconnected.connect(_on_peer_disconnected)
    print("[Lobby] Server up on :%d" % PORT)

func join_game(address: String) -> void:
    var peer = ENetMultiplayerPeer.new()
    peer.create_client(address, PORT)
    multiplayer.multiplayer_peer = peer
    multiplayer.connected_to_server.connect(_on_connected)
    multiplayer.connection_failed.connect(_on_failed)

func _on_peer_connected(id: int) -> void:
    player_connected.emit(id)
    _sync_state_to_new_peer.rpc_id(id)

@rpc("authority", "call_remote", "reliable")
func _sync_state_to_new_peer() -> void:
    # Send current world snapshot to joining peer
    var snapshot := WorldState.capture()
    WorldState.apply_snapshot.rpc(snapshot)
🔒

Unlock to see the full implementation

GET FULL ACCESS

THIS LESSON + EVERYTHING ELSE

Unlock all lessons, project files, and community access. New lessons every week.

UNLOCK THIS LESSON — $29/MONTH

Secure checkout · Cancel anytime · Instant access

← BACK TO GODOTBIT