AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Setup Multiplayer

skill-summerengine-summer-engine-agent-setup-multiplayer · by SummerEngine

Use when the user wants to add multiplayer to an existing single-player game — co-op LAN, co-op online, competitive PvP, or lobbies/matchmaking. Strong opinion that you start with Godot's high-level MultiplayerAPI plus MultiplayerSpawner and MultiplayerSynchronizer; only roll custom networking with a justified reason. Walks peer authority, RPC patterns, replication graph, and irreversible archite…

No reviews yet
0 installs
29 views
0.0% view→install

Install

$ agentstack add skill-summerengine-summer-engine-agent-setup-multiplayer

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-summerengine-summer-engine-agent-setup-multiplayer)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Setup Multiplayer? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

/setup-multiplayer — Add Multiplayer Without Repainting the Project

Overview

Multiplayer is a one-way door. Pick the wrong authority model and refactoring later is brutal. This skill enforces an opinionated path: start with Godot's high-level MultiplayerAPI + MultiplayerSpawner + MultiplayerSynchronizer + ENet. Don't roll custom networking unless you have a measured reason. Make the irreversible decisions up front, in writing.

Core principle: the architecture decision (peer authority vs server authority) is harder than the code. Lock it before touching a single RPC.

Steps

1. Ask the four blocking questions

Before any code:

> 1. What kind of multiplayer? co-op LAN / co-op online / competitive PvP / lobbies + matchmaking > 2. How many players? 2 / 2–4 / 8+ > 3. Who runs the simulation? host (one of the players) / dedicated server / authoritative peer-to-peer > 4. What's the network you're targeting? same Wi-Fi / public internet with ~80ms RTT / mobile

Wait. Each answer flips an irreversible knob.

2. Pick the authority model — irreversible decision

Lock it now. Refactoring authority is a project-killer.

| Model | Best for | Pros | Cons | |---|---|---|---| | Host authority (one player runs the sim) | Co-op (2–4 players), casual PvP, friends-only | Cheapest. No server bill. Host migration possible. | Host advantage (no lag for them). Cheating possible. | | Dedicated server authority | Competitive PvP, matchmaking, > 4 players | Fair. Anti-cheat possible. Stable. | Server bill. Need a deploy story. Higher complexity. | | Peer-to-peer with shared authority | Lock-step strategy / RTS, fighting games (rollback) | Symmetric latency. No "host advantage". | Determinism is a project on its own. Hard to debug. |

Default recommendation: host authority for co-op (2–4 players), dedicated server for everything else. If user says "competitive PvP" + "lobbies": dedicated.

State the choice explicitly:

> Going with host authority for 2–4 player co-op. The first player to start a game is the host. Their scene tree is the source of truth; clients receive replicated state and send input RPCs. Host migration is hard with this model — if the host quits, the game ends. Confirm?

3. Pick the transport

Almost always: ENetMultiplayerPeer. It's UDP with reliability layers, ships with Godot, and just works for I'm about to: > - Add a Network autoload (scripts/network.gd) that owns peer setup + spawn lifecycle. > - Convert ./World/Player from a static scene node into a MultiplayerSpawner-managed instance, spawned per peer on join. > - Add a MultiplayerSynchronizer to the player scene replicating position, rotation.y, health. > - Convert player input from local → input intent RPC sent to the authority. > - Add a basic host/join UI on the main menu. > > Authority model: host. Transport: ENet. Tick: 30 Hz input, 20 Hz position. Confirm before I touch anything?

7. Build it

a) Network autoload

scripts/network.gd:

class_name Network
extends Node

const PORT := 7000
const MAX_PLAYERS := 4

signal player_joined(peer_id: int)
signal player_left(peer_id: int)

@export var player_scene: PackedScene

var _peer: MultiplayerPeer

func host_game() -> Error:
    var enet := ENetMultiplayerPeer.new()
    var err := enet.create_server(PORT, MAX_PLAYERS - 1)
    if err != OK:
        return err
    _peer = enet
    multiplayer.multiplayer_peer = _peer
    multiplayer.peer_connected.connect(_on_peer_connected)
    multiplayer.peer_disconnected.connect(_on_peer_disconnected)
    # Host spawns its own player at id=1 (server id)
    _spawn_player(1)
    return OK

func join_game(address: String) -> Error:
    var enet := ENetMultiplayerPeer.new()
    var err := enet.create_client(address, PORT)
    if err != OK:
        return err
    _peer = enet
    multiplayer.multiplayer_peer = _peer
    return OK

func _on_peer_connected(peer_id: int) -> void:
    # Only the host spawns players. Clients receive via MultiplayerSpawner.
    if multiplayer.is_server():
        _spawn_player(peer_id)
    player_joined.emit(peer_id)

func _on_peer_disconnected(peer_id: int) -> void:
    var node_name := str(peer_id)
    var players := get_tree().get_first_node_in_group("players_root")
    if players != null and players.has_node(node_name):
        players.get_node(node_name).queue_free()
    player_left.emit(peer_id)

func _spawn_player(peer_id: int) -> void:
    var player: Node = player_scene.instantiate()
    player.name = str(peer_id)        # name = peer id; sync by name
    player.set_multiplayer_authority(peer_id)
    var players_root := get_tree().get_first_node_in_group("players_root")
    players_root.add_child(player, true)

Register as autoload in project.godot:

summer_project_setting(name="autoload/Network", value="*res://scripts/network.gd")
b) Player scene with MultiplayerSynchronizer

In the player scene, add:

Player (CharacterBody3D)
├── CollisionShape3D
├── Camera3D                         # local-only, attach in script if peer is local
├── MeshInstance3D
└── MultiplayerSynchronizer
summer_add_node(parent="./World/Players/Player", type="MultiplayerSynchronizer", name="Sync")

Configure the synchronizer to replicate position, rotation.y, health. Save as a SceneReplicationConfig.tres (NOT inline sub_resource — silent-fail trap):

# scripts/player_net.gd
extends CharacterBody3D

@export var move_speed: float = 5.0
@export var max_health: int = 100

@onready var camera: Camera3D = $Camera3D
@onready var sync: MultiplayerSynchronizer = $Sync

var health: int = 100

func _ready() -> void:
    health = max_health
    var auth := get_multiplayer_authority()
    var is_local := auth == multiplayer.get_unique_id()
    camera.current = is_local
    set_physics_process(is_local)        # only the owner runs movement
    set_process_input(is_local)

func _physics_process(delta: float) -> void:
    var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
    var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    if direction:
        velocity.x = direction.x * move_speed
        velocity.z = direction.z * move_speed
    else:
        velocity.x = move_toward(velocity.x, 0, move_speed)
        velocity.z = move_toward(velocity.z, 0, move_speed)
    if not is_on_floor():
        velocity.y -= 9.8 * delta
    move_and_slide()
    # position is replicated automatically via MultiplayerSynchronizer

@rpc("any_peer", "call_local", "reliable")
func take_damage(amount: int) -> void:
    if not multiplayer.is_server():
        return                                          # only host applies damage
    health = max(0, health - amount)
    if health == 0:
        rpc("on_died")

@rpc("authority", "call_local", "reliable")
func on_died() -> void:
    queue_free()
c) RPC patterns — the four flavors

Lock the conventions:

| RPC config | Use case | |---|---| | @rpc("authority", "call_local", "reliable") | Host tells everyone something happened (health change result, death) | | @rpc("any_peer", "call_remote", "unreliable") | Frequent input intent (movement, look) — drop is OK | | @rpc("any_peer", "call_remote", "reliable") | Rare input event (fire weapon, pick up item) — drop NOT OK | | @rpc("any_peer", "call_local", "reliable") | Player asks host to do something (request damage) |

Authority validation is mandatory. Inside any any_peer RPC, check multiplayer.get_remote_sender_id() and validate the sender owns the action.

@rpc("any_peer", "call_local", "reliable")
func request_fire() -> void:
    var sender := multiplayer.get_remote_sender_id()
    if sender != get_multiplayer_authority():
        return                       # someone else is trying to control this player; drop
    # ... actually fire
d) Lag compensation outline

Defer to a specialist skill, but flag the basics:

  • For projectiles: spawn locally on owner's machine at fire time, replicate the spawn event with a timestamp. Other peers fast-forward the projectile by their RTT.
  • For hit detection: server-authoritative + simple latency tolerance (host accepts hits within ~100ms past server time).
  • For movement: client-side prediction + server reconciliation is the standard model. Godot doesn't ship this — you write it. If users ask for "competitive PvP shooter", say: this is a deeper specialist topic and link multiplayer-and-networking/lag-compensation/SKILL.md when it ships.

8. Save and verify

summer_save_scene
summer_get_script_errors
summer_play
# user tests by running two instances locally (Project → Run Multiple Instances → 2)
summer_stop

Test checklist:

  • [ ] Two instances connect (one hosts, one joins).
  • [ ] Each instance sees its own player as local (camera attached, can move).
  • [ ] Each instance sees the other player move smoothly (sync at 20 Hz).
  • [ ] Damage RPC routes through host (not double-applied on each peer).
  • [ ] Disconnect cleans up the disconnected player's node.

Common mistakes

| Don't | Do | Why | |---|---|---| | Decide authority model halfway through | Lock it in step 2 before touching code | Refactoring authority is a project-killer | | Replicate every property | Replicate position, rotation, health, that's it | Bandwidth + bug surface | | Run physics on every peer for every player | Physics on owner only, others receive transform | Otherwise jitter and divergence | | Trust the client | Validate get_remote_sender_id() in every any_peer RPC | Cheaters route through the wrong RPC | | Inline SceneReplicationConfig sub_resource | Save as standalone .tres | Silent-fail trap (see references/mcp-tools-reference.md) | | Roll custom transport | ENet (or WebSocket for browser) | You will hit edge cases ENet already solved | | Skip authority check in damage RPC | if not multiplayer.is_server(): return at the top of damage handlers | Otherwise damage applied N times | | Spawn players on every peer | MultiplayerSpawner — host spawns, clients receive | Otherwise N copies of each player | | @rpc("any_peer") everything | Use authority for server→clients, any_peer only for client→server | Mismatched flow = exploits | | Camera replicated | Camera is local-only, set current = is_local on spawn | Each peer needs their own camera |

Anti-patterns

  • "Let's also have rollback netcode for our co-op shooter." Rollback is a 6-month project on its own. Use it for fighting games and lock-step RTS, not co-op.
  • NAT punchthrough on day one. Use Steam Networking or a relay; don't write punchthrough yourself for v1.
  • Dedicated servers without a deploy story. If you can't redeploy in < 5 min, the dedicated path isn't ready.
  • Custom serialization. Godot's variant serialization is fine. Don't write your own packing until you measure bandwidth.
  • Host migration in v1. It's a feature, not a primitive. Ship without it; add later if churn data demands.

Collaborative protocol

This skill rewires the project at the architectural level. Always ask before each block of changes. Group writes — "I'm about to add the autoload + 2 scenes + 3 scripts + InputMap entries. OK?". See references/collaborative-protocol.md.

Want a working starter?

For a known-good multiplayer scaffold (host + join UI, ENet transport, MultiplayerSpawner, replicated player), point users at:

template-id: TBD (template-co-op-online planned, see references/template-registry.md)

For now: this skill produces the scaffold inline. When the template lands, link from here.

See also

  • references/mcp-tools-reference.md — full MCP tool list (project-setting, autoload registration)
  • references/godot-version.mdMultiplayerAPI is medium-churn; SceneReplicationConfig settled in 4.0+
  • references/collaborative-protocol.md — "May I write" pattern
  • references/gd-style.md — typed GDScript conventions
  • multiplayer-and-networking/multiplayerapi-basics/SKILL.md — deeper MultiplayerAPI patterns
  • multiplayer-and-networking/client-server-pattern/SKILL.md — dedicated server architecture
  • multiplayer-and-networking/peer-replication/SKILL.md — synchronizer config patterns
  • multiplayer-and-networking/enet-vs-websocket/SKILL.md — transport choice
  • multiplayer-and-networking/lag-compensation/SKILL.md — deeper netcode for competitive (when shipped)
  • multiplayer-and-networking/state-sync/SKILL.md — selective replication patterns
  • multiplayer-and-networking/lobby-and-matchmaking/SKILL.md — lobby UI + Steam matchmaking

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.