Skip to main content

gamedev-multiplayer

Use when adding multiplayer or netcode to a game — client-server vs P2P, server authority and anti-cheat, state replication vs RPCs, prediction and reconciliation, lag compensation, plus Godot 4 / Unity NGO / Unreal wiring. NOT single-player gameplay (that is `godot`, `unity`, `unreal`), NOT matchmaking or server hosting (that is `deployment`).

소스 정보

저장소
ericrisco/rsc-harness
최근 소스 활동
2026년 7월 29일 22:07
감지된 SKILL.md 언어
영어
스타
142
포크
11

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
6 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
gamedev-multiplayer
description
Use when adding multiplayer or netcode to a game — client-server vs P2P, server authority and anti-cheat, state replication vs RPCs, prediction and reconciliation, lag compensation, plus Godot 4 / Unity NGO / Unreal wiring. NOT single-player gameplay (that is `godot`, `unity`, `unreal`), NOT matchmaking or server hosting (that is `deployment`).
tags
["multiplayer","netcode","replication","networking","prediction","authority"]
recommends
["godot","unity","unreal","gamedev-physics"]
profiles
["full"]
origin
risco
# Game multiplayer & netcode Design and wire the network layer of a game across Godot 4.x, Unity, and Unreal: choose a topology, put authority in the right place, replicate the right state, and hide latency without opening the door to cheaters. This skill owns the *networking* decisions; the engine skills own the local gameplay those decisions sit on top of. Route elsewhere when the ask is not netcode: | The ask | Route to | Why not here | | --- | --- | --- | | Single-player movement, AI, input, animation, save files | [`godot`](../godot/SKILL.md) / [`unity`](../unity/SKILL.md) / [`unreal`](../unreal/SKILL.md) | The engine skill owns local gameplay; no network dimension. | | Physics determinism, collision, character controllers | [`gamedev-physics`](../gamedev-physics/SKILL.md) | Netcode *consumes* determinism; it does not own the physics fix. | | Matchmaking, lobby backend, dedicated-server hosting, relays, DB, game-server CI | [`deployment`](../deployment/SKILL.md) | Running the fleet, not designing the in-game net layer. | | Web realtime — chat, presence, a generic WebSocket app | [`webhooks`](../webhooks/SKILL.md) | Not a game world/simulation. | | General threat modeling, authz review | [`secure-coding`](../secure-coding/SKILL.md) | This skill keeps only the game-specific anti-cheat controls. | ## Version contract — read first Target the current APIs. **Never emit these deprecated / removed APIs:** | Engine | Target | Banned (removed/legacy) | Emit instead | |---|---|---|---| | **Godot** | 4.x | `master`/`puppet`/`remote`/`sync` keywords, `rpc_config()` as a func, `rset`, `NetworkedMultiplayerENet` | `@rpc(...)` annotation, `set_multiplayer_authority()`, `ENetMultiplayerPeer`, `MultiplayerSynchronizer` | | **Unity** | Netcode for GameObjects (NGO) 1.x/2.x | UNet / HLAPI: `NetworkIdentity`, `[Command]`, `[SyncVar]`, `NetworkServer`, `UnityEngine.Networking` | NGO `NetworkObject`, `NetworkBehaviour`, `NetworkVariable<T>`, `[Rpc(SendTo.X)]` | | **Unreal** | 5.x | replicating by polling in `Tick`, skipping `WithValidation` on gameplay RPCs | `UPROPERTY(Replicated / ReplicatedUsing=...)` + `GetLifetimeReplicatedProps`, `Server`/`Client`/`NetMulticast` `UFUNCTION`s | UNet is **removed** from modern Unity — do not scaffold it. If a repo genuinely uses Mirror (a community UNet-like fork), say so; this skill targets first-party NGO. In NGO 2.x the unified `[Rpc(SendTo.Server)]` / `[Rpc(SendTo.ClientsAndHost)]` attribute is preferred; the older `[ServerRpc]` / `[ClientRpc]` still compile (and enforce `…ServerRpc` / `…ClientRpc` method-name suffixes) — cover them but prefer the unified form on new code. ## Topologies & authority Pick the topology first; everything else follows from it. - **Client-server, dedicated server** — a headless authoritative process no player controls. The gold standard for competitive/persistent games: one source of truth, hardest to cheat, costs money to run. - **Client-server, listen server** — one player's machine is also the server (host). Cheap, zero infra, but the host has a latency advantage and can tamper; fine for co-op and casual PvP. **Design so the same code runs on both** (a listen server is a dedicated server that also has a local client). - **Peer-to-peer** — no central authority; peers exchange state directly. Low infra cost and low latency between peers, but NAT traversal is painful, it is the easiest to cheat, and it needs lockstep or a rollback model to stay consistent. Reasonable for small trusted lobbies or deterministic fighting games. **Server-authoritative is the default.** The server owns the truth; clients send *intent* (inputs, requests), never results. **Never trust the client:** the client cannot set its own health, position, score, currency, or hit results — it *asks*, the server *decides*, everyone else *observes*. A client that says "I moved to X / I did 40 damage / I have 999 gold" is either lagging or cheating; treat both the same. The one thing a client legitimately owns is its own input and, by convention, cosmetic-only state. See **[references/prediction-and-latency.md](references/prediction-and-latency.md)** for how to keep this responsive. ## State replication vs RPCs Two complementary tools — use the right one: - **State replication** = *continuous* synchronized values (health, position, ammo, team). The framework watches a variable and pushes changes to relevant peers; late-joiners get the current value automatically. Use for anything a client needs the *latest* of. It is idempotent and self-healing on packet loss. - **RPCs (remote procedure calls)** = *one-shot events* ("fire weapon", "play explosion at P", "open door"). Fire-and-forget; a dropped unreliable RPC is simply lost. Use for events, **never** for state you need to stay consistent — a missed "you died" RPC leaves a client desynced forever; a replicated `isDead` bool self-corrects. **Rule of thumb:** replicate *nouns* (state), RPC *verbs* (events). What to replicate: only what other peers must see, at the coarsest rate that still looks right — position/rotation of visible actors, gameplay stats, animation state flags. Do **not** replicate values a peer can derive locally, purely cosmetic particles, or high-frequency data no one observes. **Ownership / authority** decides *who may write*. Every networked object has an owner (a peer id) and an authority (usually the server). Writes to replicated state must come from the authority; ownership lets a client drive *its own* pawn's input while the server still validates the outcome. Getting ownership wrong is the #1 cause of "it works for the host but not the client" bugs. ## The hard problems Latency (round-trip time, RTT) is the enemy; jitter and packet loss make it worse. Four techniques, covered in depth in **[references/prediction-and-latency.md](references/prediction-and-latency.md)**: 1. **Client-side prediction** — the owning client applies its own input *immediately* instead of waiting a full RTT for the server, so local movement feels instant. 2. **Server reconciliation** — the server is still authoritative; the client tags each input with a sequence number, and when the authoritative state arrives it *replays* any inputs the server hadn't processed yet. Mismatch → a correction (the visible "rubber-band"); minimize it, don't hide it. 3. **Snapshot interpolation** — *remote* entities are rendered ~100 ms in the past, interpolating between the last two received snapshots, so other players move smoothly instead of teleporting between updates. 4. **Lag compensation** — for hitscan/instant hits, the server rewinds other entities to where the shooter *saw* them (at the shooter's render time) before testing the hit. Fairer for the shooter; occasionally "shot behind a wall" for the victim. A deliberate tradeoff, not a bug. **Tick rate / netrate:** the server simulates at a fixed tick (e.g. 20–64 Hz) and sends state at a *send rate* that may be lower than its sim rate and decoupled from client render FPS. Never tie gameplay to frame rate on a networked game — simulate on a fixed timestep, interpolate for rendering. ## Per-engine mapping Short form here; full patterns in the references. All snippets are current-API. ### Godot 4.x — high-level multiplayer `ENetMultiplayerPeer` for transport, `@rpc` for events, `MultiplayerSynchronizer` for state, and `MultiplayerSpawner` to replicate object spawns. Authority via `set_multiplayer_authority(peer_id)`. ```gdscript # Host / join func host(port := 9999) -> void: var peer := ENetMultiplayerPeer.new() peer.create_server(port, 8) multiplayer.multiplayer_peer = peer func join(ip: String, port := 9999) -> void: var peer := ENetMultiplayerPeer.new() peer.create_client(ip, port) multiplayer.multiplayer_peer = peer # RPC: params are optional and order-free. any_peer lets clients call it; # authority (default) means only the multiplayer authority may call it. @rpc("any_peer", "call_local", "reliable") func request_fire(target: Vector3) -> void: if not multiplayer.is_server(): return # server validates var shooter := multiplayer.get_remote_sender_id() _resolve_shot(shooter, target) # server decides the result ``` - `set_multiplayer_authority(id)` on a node; check with `is_multiplayer_authority()`. A player scene usually gives its input node authority to the owning peer while the server keeps authority over health. - `MultiplayerSynchronizer` streams a chosen property list (position, etc.) from the authority to peers; set its replication interval and visibility (`set_visibility_for(peer, bool)`) to scope bandwidth. - `MultiplayerSpawner` replicates instancing of scenes under a path so late peers get existing objects. - Details, prediction wiring, and the full player-scene pattern: **[references/godot-high-level-multiplayer.md](references/godot-high-level-multiplayer.md)**. ### Unity — Netcode for GameObjects `NetworkObject` (the identity, spawned via `Spawn()`), `NetworkBehaviour` (networked scripts), `NetworkVariable<T>` (replicated state), and RPCs. `NetworkManager.Singleton.StartHost/StartServer/StartClient`. ```csharp public class Player : NetworkBehaviour { // Default perms: everyone reads, only the SERVER writes → authoritative health. public NetworkVariable<int> Health = new(100); [Rpc(SendTo.Server)] // client → server: an intent void FireRpc(Vector3 target) { // runs on the server; validate, then apply and let NetworkVariables replicate the result ResolveShot(OwnerClientId, target); } [Rpc(SendTo.ClientsAndHost)] // server → everyone: an event void PlayMuzzleFxRpc(Vector3 at) { /* cosmetic only */ } } ``` - Ownership/role: `IsOwner`, `IsServer`, `IsClient`, `IsHost`, `OwnerClientId`. Only owners should send owner-intent RPCs; the server validates regardless. - `NetworkVariable<T>(value, readPerm, writePerm)` — `NetworkVariableWritePermission.Server` (default) vs `.Owner`; subscribe via `OnValueChanged`. Use `NetworkTransform` for pos/rot sync. - Prefer the unified `[Rpc(SendTo.X)]`; legacy `[ServerRpc]`/`[ClientRpc]` still work but need the method-name suffixes. Full patterns: **[references/unity-and-unreal-netcode.md](references/unity-and-unreal-netcode.md)**. ### Unreal — Actor replication Set `AActor::bReplicates = true` (or `SetReplicates(true)`). Replicate properties with `UPROPERTY` and register them in `GetLifetimeReplicatedProps`; use RPC `UFUNCTION` specifiers for events. ```cpp UPROPERTY(ReplicatedUsing = OnRep_Health) // RepNotify fires on clients when it changes float Health = 100.f; UFUNCTION(Server, Reliable, WithValidation) // client → server intent (+ validation) void ServerFire(FVector Target); UFUNCTION(NetMulticast, Unreliable) // server → all: cosmetic event void MulticastMuzzleFx(FVector At); void AMyPawn::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const { Super::GetLifetimeReplicatedProps(Out); DOREPLIFETIME(AMyPawn, Health); // or DOREPLIFETIME_CONDITION for push/owner-only } ``` - Authority check: `HasAuthority()` (server or standalone); roles `ROLE_Authority`, `ROLE_AutonomousProxy` (the owning client), `ROLE_SimulatedProxy` (observed elsewhere). - **Relevancy** keeps bandwidth sane: `NetCullDistanceSquared`, `bAlwaysRelevant`, `IsNetRelevantFor`, and dormancy (`SetNetDormancy`) stop replicating actors a client can't perceive. Movement replicates via `CharacterMovementComponent`, which already does prediction+reconciliation for you. - RepNotify vs Multicast, GAS/Network Prediction notes: **[references/unity-and-unreal-netcode.md](references/unity-and-unreal-netcode.md)**. ## Security / anti-cheat basics - **Validate every client input on the server** — bounds-check movement deltas, cooldowns, line-of-sight, and resource costs. Reject the impossible; never assume a well-behaved client. - **Send a client only what it may know.** Don't replicate enemy positions through walls (wallhack fuel), full inventories of others, or hidden RNG seeds — scope with relevancy/visibility. - Rate-limit and sanity-check RPCs; a client spamming `ServerFire` faster than the weapon allows is caught by server-side cooldowns, not client-side ones. - Encryption/DTLS stops packet tampering on the wire but does **not** stop a modified client — only server authority does. Treat anti-cheat as defense in depth, not a product this skill ships. ## Testing multiplayer locally - **Godot:** enable *Debug → Run Multiple Instances* (2–4 windows) to host+join on one machine. - **Unity:** *Multiplayer Play Mode* (or ParrelSync) runs several virtual players from one project; Network Simulator injects latency/loss. - **Unreal:** PIE *Number of Players* + *Net Mode* (Play As Listen Server / Client), and *Net PktLag* / packet-emulation console vars to fake latency. - Always test **with simulated latency and packet loss**, not just on localhost — localhost hides every prediction/reconciliation bug. Test the client path, not only the host's. ## Anti-patterns | Anti-pattern | Why it bites | Do instead | | --- | --- | --- | | Writing net code before the topology is chosen | Authority and cost model change everything downstream | Pick dedicated / listen / P2P first, and make the *same code* run on server and listen host | | Trusting a client-reported result (health, position, score, hit) | That is the cheat surface, and lag looks identical to cheating | Clients send intent only; the server validates and owns all gameplay state | | RPCing state that must stay consistent | A dropped unreliable event desyncs that client permanently | Replicate nouns (state), RPC verbs (events) — replicated values self-correct | | Ownership/authority left implicit | The #1 cause of "works for the host, breaks for the client" | Set authority/ownership explicitly on spawn | | Forgetting the listen-server host is also a client | Authority-only logic double-executes on the host | Guard it behind `is_server()` / `HasAuthority()` | | Making the owning client wait a full RTT for its own input | Movement feels sluggish and unresponsive | Predict locally + reconcile; snapshot-interpolate remote entities | | Client-side hit detection for hitscan | Trivially spoofed, and unfair to the higher-ping shooter | Server-side lag compensation: rewind to the shooter's render time | | Tying simulation to render FPS | Sim diverges between machines at different frame rates | Fixed timestep on the sim, interpolate on the render | | Replicating everything, every tick, to everyone | Melts bandwidth and leaks wallhack fuel | Scope by relevancy/visibility and send rate; rate-limit RPCs | | Testing only on localhost | 0 ms RTT masks the exact desync/rubber-band bugs players will hit | Inject latency + packet loss across instances, exercising the client path | | Emitting UNet/HLAPI, Godot 3.x `master`/`puppet`/`rset`, or `Tick`-polled Unreal replication | Removed or legacy — it will not compile or will silently underperform | The current APIs in the version contract above |
GitHub에서 보기