Godot 4.x ships with a powerful built-in networking stack centred on ENetMultiplayerPeer — a UDP-based MultiplayerPeer implementation that handles reliable delivery, packet sequencing, and bandwidth management out of the box. You do not need a paid backend or third-party middleware to get a working online game running in a single session.
Table of Contents
This tutorial covers the entire high-level multiplayer system: the complete ENetMultiplayerPeer API (including exact signatures for create_server() and create_client()), MultiplayerSpawner for replicating player scenes, MultiplayerSynchronizer for streaming continuous state, and the @rpc decorator for one-off events like dealing damage. Everything is current for Godot 4.4 and 4.6 stable.
Quick Answer
To add multiplayer to a Godot 4 project: create an ENetMultiplayerPeer, call peer.create_server(port) to host or peer.create_client(address, port) to join, assign it to multiplayer.multiplayer_peer, then use MultiplayerSpawner to replicate player scenes and MultiplayerSynchronizer to stream properties such as position. Use @rpc-decorated functions for discrete events. The server peer always receives the ID 1; all clients get unique IDs assigned by the server on connect.
ENetMultiplayerPeer API Reference
ENetMultiplayerPeer inherits from Object → RefCounted → PacketPeer → MultiplayerPeer → ENetMultiplayerPeer. It wraps the ENet library (UDP only, not TCP) and supports three connection modes: server, client, and mesh. After calling one of the create_* methods, assign the instance to multiplayer.multiplayer_peer and Godot’s high-level API takes over signal dispatch and RPC routing automatically. The host property (type ENetConnection) exposes the underlying connection object for advanced per-peer configuration after initialisation.
create_server(port: int, max_clients: int = 32, max_channels: int = 0, in_bandwidth: int = 0, out_bandwidth: int = 0) → Error. Opens a listening socket on the given port (0–65535; ports below 1024 may require elevated OS permissions on Linux and macOS). max_clients caps simultaneous connections at up to 4095 — the default of 32 is appropriate for most indie titles. max_channels sets the number of ENet logical channels allocated for the connection; 0 lets ENet use its own default. in_bandwidth and out_bandwidth throttle throughput in bytes per second; 0 means unlimited. Returns OK on success, ERR_ALREADY_IN_USE if the peer already has an open connection, or ERR_CANT_CREATE if the port cannot be bound. Always check the return value — a silent failure looks identical to a working server until a client tries to connect.
create_client(address: String, port: int, channel_count: int = 0, in_bandwidth: int = 0, out_bandwidth: int = 0, local_port: int = 0) → Error. Connects to the server at address (IPv4, IPv6, or a fully qualified domain name) on port. channel_count, in_bandwidth, and out_bandwidth mirror the server-side parameters. local_port is optional and useful for NAT traversal scenarios — leave it at 0 to let the OS assign a free ephemeral port automatically. Returns OK, ERR_ALREADY_IN_USE, or ERR_CANT_CREATE with the same semantics as create_server(). Note that a return of OK means the connection attempt was initiated, not that the handshake is complete; listen for the multiplayer.connected_to_server signal to confirm the session is live.
set_bind_ip(ip: String) → void. Must be called before create_server(). The default value is “*”, which binds all available network interfaces. Pass “127.0.0.1” to restrict the server to localhost during development, or a specific LAN address such as “192.168.1.10” to bind a single NIC. This method has no effect once the server socket is already open.
create_mesh(unique_id: int) → Error and add_mesh_peer(peer_id: int, host: ENetConnection) → Error. Mesh mode has no designated server — every peer is equal. Call create_mesh() with a unique integer ID to identify this peer, then wire each remote peer with add_mesh_peer(). The ENetConnection passed to add_mesh_peer() must have exactly one peer in the STATE_CONNECTED state. Mesh topology reduces reliance on a central host but requires you to handle peer discovery and connection brokering yourself.
get_peer(id: int) → ENetPacketPeer returns the low-level ENetPacketPeer for a given peer ID, which lets you read per-peer statistics or apply per-connection bandwidth limits. This is an advanced feature — most games using the high-level API never need it directly.
Step-by-Step: Building Your First Multiplayer Game
Step 1 — Plan your architecture. Start with client-server rather than peer-to-peer: one player hosts (the server, always peer ID 1) and others connect as clients. The server holds authority over game state; clients send inputs and receive updates. This asymmetry is what Godot’s high-level multiplayer tooling assumes by default, and it makes cheat prevention straightforward. Bolting multiplayer onto 50 scenes after the fact is genuinely painful — design for it from the start.
Step 2 — Create a NetworkManager. Add a NetworkManager.tscn scene with a plain Node root and a GDScript. Declare var peer = ENetMultiplayerPeer.new(). To host: var err = peer.create_server(1234, 4) — port 1234, max 4 clients — assert(err == OK, “Server failed to start”), then multiplayer.multiplayer_peer = peer. To join: var err = peer.create_client(“SERVER_IP”, 1234), assert(err == OK, “Client failed to connect”), multiplayer.multiplayer_peer = peer. Connect multiplayer.peer_connected and multiplayer.peer_disconnected to track who enters and leaves the session.
Step 3 — Add a MultiplayerSpawner. In your main game scene, add a MultiplayerSpawner and set its Spawn Path to the container where players will live (for example /root/Game/Players). Add your player scene to the Auto Spawn List in the inspector. When the server instances a player scene under that path, Godot automatically replicates the spawn to every connected client. Call player_node.set_multiplayer_authority(peer_id) immediately after spawning so the correct machine controls the correct character.
Step 4 — Sync with MultiplayerSynchronizer. Inside the Player scene, add a MultiplayerSynchronizer node. In its inspector open the Replicated Properties list and add position (and rotation if your game needs it). The synchronizer sends delta updates every network tick — no manual packet code required for continuous movement data.
Step 5 — Use @rpc for events. Decorate a function with @rpc(“any_peer”, “call_local”, “reliable”) for important one-off events like taking damage or collecting an item. Call it with my_function.rpc() to invoke on all peers simultaneously. Switch to “unreliable” mode for high-frequency loss-tolerant data (cursor position, cosmetic animations) where a dropped packet is not harmful.
Step 6 — Test with multiple instances. Open Debug > Run Multiple Instances and set the count to 2. Launch the project, have one window host and the other join on 127.0.0.1. The Debugger’s Network profiler tab shows live RPC calls and peer activity, which is invaluable for diagnosing timing bugs and spotting unintended authority violations.
Core Concepts Every Godot Multiplayer Dev Needs
Multiplayer authority determines which peer’s inputs and logic are trusted for a given node. Peer ID 1 (the server) owns every node by default. After spawning a player, call player_node.set_multiplayer_authority(peer_id) to hand ownership to that player’s machine. Guard input processing with if not is_multiplayer_authority(): return so only the owning peer moves the character — everyone else just displays the replicated position.
MultiplayerSynchronizer vs. @rpc: the synchronizer is best for continuous state (position, health, animation blend values). The @rpc system is better for events (door opened, bullet fired, score increment). Assigning both to the same property creates a race condition — the synchronizer wins on the next tick and silently overwrites any @rpc-driven change. Pick one per piece of data and be consistent.
Peer IDs are stable within a session but are not a persistent identity. Store player data in a Dictionary keyed by peer ID on the server and clean it up in the peer_disconnected handler. Do not persist peer IDs to disk or treat them as account identifiers — generate a separate UUID for persistent player identity.
Choosing the Right Transport: ENet vs. WebSocket vs. WebRTC
Godot 4 ships three MultiplayerPeer implementations. ENetMultiplayerPeer (UDP-based) is the right choice for desktop and mobile games — it handles reliable and unreliable channels, packet sequencing, and bandwidth shaping without any extra setup. WebSocketMultiplayerPeer works for HTML5 exports where browsers block raw UDP, but adds measurable latency because WebSocket sits on top of TCP. WebRTCMultiplayerPeer enables true peer-to-peer connections that run natively in the browser, but requires you to host a signaling server to broker the initial handshake.
The ENetMultiplayerPeer high-level layer works well for small concurrent sessions — two to roughly eight players in cooperative or party-style games. For competitive play, high player counts, matchmaking, or cross-region relay, you will need an external service on top of ENet for those features, but ENet remains the right transport for the in-game data itself.
Common Mistakes and How to Avoid Them
Not checking the Error return from create_server() or create_client(). Both methods return an Error enum value. Ignoring it means your game appears to start normally but no networking occurs — always assert or log on a non-OK result.
Calling set_bind_ip() after create_server(). It has no effect once the socket is already open. Call it first, then create_server().
Forgetting set_multiplayer_authority() after spawning a player. Without it the server owns every player node, so clients cannot move their own characters. This is the single most common reason a multiplayer demo works perfectly on the host but feels broken on clients.
Using MultiplayerSynchronizer and @rpc on the same property. Pick one. The synchronizer pushes an update every network tick and silently overwrites any @rpc-driven value, creating a hard-to-reproduce race condition.
godot-4-enetmultiplayerpeer-multiplayer FAQs
What is ENetMultiplayerPeer in Godot 4?
ENetMultiplayerPeer is Godot 4’s built-in UDP networking class. It implements the MultiplayerPeer interface using the ENet library and supports server, client, and mesh topologies. Assign it to multiplayer.multiplayer_peer after calling create_server() or create_client() and Godot’s high-level API handles signal dispatch, RPC routing, and spawner/synchronizer replication automatically. Its inheritance chain is: Object → RefCounted → PacketPeer → MultiplayerPeer → ENetMultiplayerPeer.
What are the full signatures for create_server() and create_client()?
create_server(port: int, max_clients: int = 32, max_channels: int = 0, in_bandwidth: int = 0, out_bandwidth: int = 0) → Error. create_client(address: String, port: int, channel_count: int = 0, in_bandwidth: int = 0, out_bandwidth: int = 0, local_port: int = 0) → Error. Both return OK on success, ERR_ALREADY_IN_USE if the peer is already connected, or ERR_CANT_CREATE if the socket could not be opened. Call set_bind_ip() before create_server() if you need to restrict the listening interface.
Where is the official Godot 4 ENetMultiplayerPeer documentation?
The official class reference is at docs.godotengine.org/en/stable/classes/class_enetmultiplayerpeer.html. The high-level multiplayer guide is at docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html. Both are part of the free, open Godot Engine documentation and cover all current stable releases.
Do I need a dedicated server to run Godot 4 multiplayer?
No. One player’s machine can act as the host using the same client-server model — this is called a listen server. For competitive or high-player-count games, a headless dedicated server (exported with the –headless flag) is preferable for fairness and uptime, but it is not required to get started or ship a small cooperative game.
Can I use ENetMultiplayerPeer for browser (HTML5) games?
No — browsers block raw UDP, so ENetMultiplayerPeer does not work in HTML5 exports. Use WebSocketMultiplayerPeer for browser targets. If you need peer-to-peer browser play without a relay server, WebRTCMultiplayerPeer is the right choice, but you will need to run a signaling server to broker the initial connection.
Get More from godot-4-enetmultiplayerpeer-multiplayer
Log the coasters, stadiums, and venues you’ve experienced, rate godot-4-enetmultiplayerpeer-multiplayer, and see what your friends thought. Get the ThrillZing app.