godot-multiplayer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Godot Multiplayer (4.x high-level)

Godot多人游戏(4.x高级版)

Connect peers, call functions remotely with
@rpc
, assign authority, and replicate state with
MultiplayerSpawner
/
MultiplayerSynchronizer
. Targets Godot 4.3+ (ENet). Treat all client input as untrusted; keep the server authoritative.
连接对等端,使用
@rpc
远程调用函数,分配权限,并通过
MultiplayerSpawner
/
MultiplayerSynchronizer
复制状态。面向Godot 4.3+(基于ENet)。将所有客户端输入视为不可信;保持服务器的权威性。

When to use

适用场景

  • Use when adding networked multiplayer: hosting/joining over ENet, calling RPCs, assigning per-node authority, or auto-spawning/syncing nodes across peers.
When not to use: local split-screen (no networking); raw TCP/UDP/WebSocket protocol work (low-level
PacketPeer
); HTTP requests. For save/persistence →
save-systems
.
  • 适用于添加联网多人游戏功能的场景:通过ENet托管/加入游戏、调用RPC、为每个节点分配权限,或在对等端之间自动生成/同步节点。
不适用场景:本地分屏游戏(无需网络);底层TCP/UDP/WebSocket协议开发(使用底层
PacketPeer
);HTTP请求。如需保存/持久化功能,请参考
save-systems

Core workflow

核心工作流程

  1. Create a peer (
    ENetMultiplayerPeer
    ), call
    create_server(port, max)
    or
    create_client(ip, port)
    , and assign it to
    multiplayer.multiplayer_peer
    . The server's unique ID is always
    1
    ; clients get random positive IDs.
  2. Handle connection signals on
    multiplayer
    :
    peer_connected(id)
    ,
    peer_disconnected(id)
    ,
    connected_to_server
    ,
    connection_failed
    ,
    server_disconnected
    .
  3. Define RPCs with
    @rpc(...)
    . Call them on a
    Callable
    via
    rpc()
    (all peers) or
    rpc_id(peer_id)
    (one peer). Inside,
    multiplayer.get_remote_sender_id()
    tells you who sent it.
  4. Keep RPC signatures identical on every peer that runs the script — Godot checksums all
    @rpc
    methods in a script; mismatches break silently.
  5. Assign authority per node with
    set_multiplayer_authority(id)
    ; gate input/RPCs by
    is_multiplayer_authority()
    .
  6. Replicate state with
    MultiplayerSpawner
    (auto-instances scenes on clients) and
    MultiplayerSynchronizer
    (auto-syncs selected properties).
  7. Validate on the server. Don't trust client-reported positions/results.
  1. 创建对等端
    ENetMultiplayerPeer
    ),调用
    create_server(port, max)
    create_client(ip, port)
    ,并将其分配给
    multiplayer.multiplayer_peer
    。服务器的唯一ID始终为
    1
    ;客户端会获得随机的正整数ID。
  2. 处理连接信号:监听
    multiplayer
    上的
    peer_connected(id)
    peer_disconnected(id)
    connected_to_server
    connection_failed
    server_disconnected
    信号。
  3. 定义RPC:使用
    @rpc(...)
    注解。通过
    Callable
    调用
    rpc()
    (发送给所有对等端)或
    rpc_id(peer_id)
    (发送给单个对等端)。在函数内部,可通过
    multiplayer.get_remote_sender_id()
    获取发送方ID。
  4. 保持RPC签名一致:运行该脚本的所有对等端上,RPC方法的签名必须完全相同——Godot会对脚本中所有
    @rpc
    方法进行校验和验证;不匹配会导致静默失败。
  5. 为节点分配权限:通过
    set_multiplayer_authority(id)
    为每个节点设置权限;通过
    is_multiplayer_authority()
    控制输入/RPC的执行。
  6. 复制状态:使用
    MultiplayerSpawner
    (在客户端自动实例化场景)和
    MultiplayerSynchronizer
    (自动同步选定属性)。
  7. 在服务器端验证:不要信任客户端上报的位置/结果。

Patterns

模式示例

1. Host or join (ENet)

1. 托管或加入游戏(ENet)

gdscript
const PORT := 7000
const MAX_PLAYERS := 8

func host() -> void:
    var peer := ENetMultiplayerPeer.new()
    var err := peer.create_server(PORT, MAX_PLAYERS)
    if err != OK:
        push_error("Cannot host: %s" % err); return
    multiplayer.multiplayer_peer = peer
    multiplayer.peer_connected.connect(_on_peer_connected)

func join(ip := "127.0.0.1") -> void:
    var peer := ENetMultiplayerPeer.new()
    peer.create_client(ip, PORT)
    multiplayer.multiplayer_peer = peer
    multiplayer.connected_to_server.connect(func(): print("connected"))

func leave() -> void:
    multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
gdscript
const PORT := 7000
const MAX_PLAYERS := 8

func host() -> void:
    var peer := ENetMultiplayerPeer.new()
    var err := peer.create_server(PORT, MAX_PLAYERS)
    if err != OK:
        push_error("Cannot host: %s" % err); return
    multiplayer.multiplayer_peer = peer
    multiplayer.peer_connected.connect(_on_peer_connected)

func join(ip := "127.0.0.1") -> void:
    var peer := ENetMultiplayerPeer.new()
    peer.create_client(ip, PORT)
    multiplayer.multiplayer_peer = peer
    multiplayer.connected_to_server.connect(func(): print("connected"))

func leave() -> void:
    multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()

2. RPCs: client sends input to the server (any_peer, call_local)

2. RPC:客户端向服务器发送输入(any_peer, call_local)

gdscript
func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("fire") and is_multiplayer_authority():
        request_fire.rpc_id(1)          # send only to the server (id 1)
gdscript
func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("fire") and is_multiplayer_authority():
        request_fire.rpc_id(1)          # 仅发送给服务器(ID为1)

Clients may call this; it runs on the server (and locally if server is a player).

客户端可调用此函数;它会在服务器上运行(如果服务器同时是玩家,也会在本地运行)。

@rpc("any_peer", "call_local", "reliable") func request_fire() -> void: var sender := multiplayer.get_remote_sender_id() if not _can_fire(sender): # server-side validation return spawn_projectile.rpc(sender) # tell everyone to spawn it
@rpc("authority", "call_local", "reliable") func spawn_projectile(owner_id: int) -> void: _do_spawn(owner_id)
undefined
@rpc("any_peer", "call_local", "reliable") func request_fire() -> void: var sender := multiplayer.get_remote_sender_id() if not _can_fire(sender): # 服务器端验证 return spawn_projectile.rpc(sender) # 通知所有对等端生成 projectile
@rpc("authority", "call_local", "reliable") func spawn_projectile(owner_id: int) -> void: _do_spawn(owner_id)
undefined

3. Per-node authority (each player controls their own avatar)

3. 按节点分配权限(每个玩家控制自己的角色)

gdscript
extends CharacterBody2D

func _ready() -> void:
    # The node name is the owning peer's id; that peer is the authority.
    set_multiplayer_authority(name.to_int())

func _physics_process(delta: float) -> void:
    if not is_multiplayer_authority():
        return                          # only the owner reads input & moves
    velocity = Input.get_vector("left", "right", "up", "down") * 200.0
    move_and_slide()
gdscript
extends CharacterBody2D

func _ready() -> void:
    # 节点名称为所属对等端的ID;该对等端拥有权限。
    set_multiplayer_authority(name.to_int())

func _physics_process(delta: float) -> void:
    if not is_multiplayer_authority():
        return                          # 仅所有者可读取输入并移动角色
    velocity = Input.get_vector("left", "right", "up", "down") * 200.0
    move_and_slide()

4. MultiplayerSynchronizer config (editor + replication)

4. MultiplayerSynchronizer配置(编辑器 + 复制)

gdscript
undefined
gdscript
undefined

Add a MultiplayerSynchronizer child; in its Replication editor add the properties to

添加一个MultiplayerSynchronizer子节点;在其复制编辑器中添加需要同步的属性(例如position、velocity)。为每个属性设置“Sync”/“Spawn”标志。通过代码可设置可见范围:

sync (e.g. position, velocity). Set "Sync"/"Spawn" flags per property. From code you

can scope visibility:

@onready var sync: MultiplayerSynchronizer = $MultiplayerSynchronizer
func _ready() -> void: # Only replicate this node to a specific peer (e.g. private info). sync.set_visibility_for(target_peer_id, true)
undefined
@onready var sync: MultiplayerSynchronizer = $MultiplayerSynchronizer
func _ready() -> void: # 仅向特定对等端复制此节点(例如私有信息)。 sync.set_visibility_for(target_peer_id, true)
undefined

Pitfalls

常见陷阱

  • RPC signature checksum. Every
    @rpc
    method in a script must exist with the same declaration on both client and server builds — even unused ones. A mismatch causes errors that may point at the wrong function. Argument names/count are not checked, but the set of RPCs and their annotations are.
  • Default
    @rpc
    is
    "authority"
    .
    Clients calling it are ignored unless you set
    "any_peer"
    . Use
    "call_local"
    so the host (also a player) runs it too.
  • NodePaths must match across peers. RPC routing uses the node's path/name; spawn nodes with identical names on all peers (use
    MultiplayerSpawner
    or
    add_child(node, true)
    for readable, deterministic names).
  • Trusting the client. Never let clients set authoritative state (health, position, hits) directly. Send intent, validate on the server, then broadcast results.
  • RPC on non-Node classes fails.
    @rpc
    methods must be on
    Node
    -derived classes, not plain
    Resource
    /
    RefCounted
    .
  • RPCs don't serialize Objects/Callables. Pass plain data (ints, strings, arrays, dictionaries, PackedArrays).
  • Forgetting to reset the peer. To disconnect cleanly, set
    multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
    .
  • Android needs INTERNET permission in the export preset or all networking is blocked.
  • RPC签名校验和:脚本中的每个
    @rpc
    方法必须在客户端和服务器构建中具有相同的声明——即使是未使用的方法。不匹配会导致错误,且错误信息可能指向错误的函数。参数名称/数量不会被检查,但RPC集合及其注解会被检查。
  • 默认
    @rpc
    权限为
    "authority"
    :除非设置为
    "any_peer"
    ,否则客户端调用会被忽略。使用
    "call_local"
    可让主机(同时作为玩家)也执行该函数。
  • NodePath必须在对等端之间匹配:RPC路由使用节点的路径/名称;在所有对等端上生成具有相同名称的节点(使用
    MultiplayerSpawner
    add_child(node, true)
    以获得可读、确定的名称)。
  • 信任客户端:永远不要让客户端直接设置权威性状态(生命值、位置、命中结果)。应发送操作意图,在服务器端验证后,再广播结果。
  • 非Node类的RPC调用会失败
    @rpc
    方法必须定义在继承自
    Node
    的类中,不能是普通的
    Resource
    /
    RefCounted
    类。
  • RPC无法序列化Objects/Callables:传递纯数据(整数、字符串、数组、字典、PackedArrays)。
  • 忘记重置对等端:要干净地断开连接,需设置
    multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
  • Android需要INTERNET权限:在导出预设中添加INTERNET权限,否则所有网络功能会被阻止。

References

参考资料

  • For
    MultiplayerSpawner
    setup, transfer modes/channels,
    SceneMultiplayer
    authentication (
    auth_callback
    /
    complete_auth
    ), a lobby skeleton, and dedicated-server export notes, read
    references/replication-and-rpc.md
    .
  • 关于
    MultiplayerSpawner
    设置、传输模式/通道、
    SceneMultiplayer
    身份验证(
    auth_callback
    /
    complete_auth
    )、大厅框架以及专用服务器导出说明,请阅读
    references/replication-and-rpc.md

Related skills

相关技能

  • godot-nodes-scenes
    — instancing the scenes that get spawned/synced.
  • godot-signals-groups
    — connection signals and event flow.
  • godot-export
    — exporting a headless dedicated server build.
  • godot-nodes-scenes
    —— 实例化需要生成/同步的场景。
  • godot-signals-groups
    —— 连接信号和事件流。
  • godot-export
    —— 导出无头专用服务器构建。