diagram-projectExample (P2P) Host-Client - Unity NGO

Netcode for GameObjects: P2P Host-Client Setup Guide

This guide provides a complete, step-by-step implementation for running Enemy Masses in a Peer-to-Peer Host-Client multiplayer scenario using Unity Netcode for GameObjects (NGO) 2.7.0.


Overview

In a P2P Host-Client model:

  • One player acts as the Host (runs both server + client logic)

  • Other players connect as Clients

  • No dedicated server required — great for playing with friends

┌─────────────────────────────────────────────────────────────┐
│                    HOST (Player 1's PC)                     │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │   Client    │  │   Server    │  │  Enemy      │        │
│  │   Logic     │  │   Logic     │  │  Masses     │        │
│  │             │  │  (Validates │  │  Runtime    │        │
│  │  (Renders,  │  │   commands, │  │             │        │
│  │   Input)    │  │   damage)   │  │             │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
│        IsServer = true    IsClient = true    IsHost = true │
└─────────────────────────────────────────────────────────────┘

                         LAN / Internet

┌─────────────────────────────────────────────────────────────┐
│                   CLIENT (Player 2's PC)                    │
│                                                             │
│  ┌─────────────┐              ┌─────────────┐              │
│  │   Client    │              │  Enemy      │              │
│  │   Logic     │              │  Masses     │              │
│  │             │              │  Runtime    │              │
│  │  (Renders,  │              │             │              │
│  │   Input)    │              │             │              │
│  └─────────────┘              └─────────────┘              │
│                                                             │
│        IsServer = false   IsClient = true   IsHost = false │
└─────────────────────────────────────────────────────────────┘

Prerequisites

Required Packages

If Package Manager resolves a newer 2.4.x transport for NGO 2.7, use that resolved version.

Required Components

  • Unity 6 LTS or newer

  • Enemy Masses 1.4.0+

  • Basic understanding of NGO concepts (NetworkBehaviour, RPCs)

Enemy Masses ships with built-in network-agnostic events — no modification to the source code is required. You simply subscribe to events and route them through your networking layer.


Game Mode Considerations

Enemy Masses supports different multiplayer game types with varying synchronization strategies:

RTS / Strategy Games

  • Players control specific factions — ownership matters

  • Commands synced — move, attack, stop commands sent to server

  • Moderate position sync — periodic corrections (0.5s intervals)

  • Strict validation — verify ownership before executing commands

Co-op / Horde / Survivor Games (e.g., Vampire Survivors-style)

  • All enemies are AI-controlled — no player ownership of enemies

  • Player positions synced — enemies target nearest synced player

  • Relaxed validation — generous distance tolerances for hit detection

  • Higher position sync rate — more frequent updates (0.1-0.2s) for smoother enemy movement

Key Difference: Command-Based vs Position-Sync

Approach
Best For
How It Works

Command-Based

RTS games

Clients send move/attack commands; server executes; positions drift but periodically correct

Position-Sync

Co-op/Horde

Server runs all AI; broadcasts positions; clients are visual-only for enemies

For co-op games, use the Position-Sync approach with higher stateSyncInterval frequency:


Step 1: Project Setup

1.1 Install Packages

  1. Open Window → Package Manager

  2. Click + → Add package by name

  3. Add com.unity.netcode.gameobjects version 2.7.0

  4. Unity Transport will be installed automatically

1.2 Create NetworkManager

  1. Create empty GameObject: GameObject → Create Empty

  2. Name it NetworkManager

  3. Add component: Netcode → NetworkManager

  4. Configure the NetworkManager:

1.3 Add Unity Transport

  1. Select NetworkManager GameObject

  2. Add component: Netcode → UnityTransport

  3. Configure for LAN/P2P:

Note: Address is the client connect address (use the host's LAN/Public IP for clients). Server Listen Address should be 0.0.0.0 when hosting.


Step 2: Create Network Authority System

2.1 Create the Main Authority Script

Create a new script EnemyMassesNetworkAuthority.cs:


Step 3: Create the Network Authority Prefab

3.1 Create the Prefab

  1. Create empty GameObject: EnemyMassesNetworkAuthority

  2. Add NetworkObject component

  3. Add EnemyMassesNetworkAuthority script

  4. Configure references in inspector

  5. Save as Prefab in Assets/Prefabs/Network/

3.2 Register as Network Prefab

  1. Select NetworkManager in scene

  2. In Network Prefabs list, add your prefab

  3. Make sure the prefab is in the list before the game starts


Step 4: Create Host/Join UI

Create a simple UI for hosting and joining games:


Step 5: Use Built-in Network Events

Enemy Masses ships with network-agnostic events that fire when key gameplay actions occur. These events use plain C# delegates with no networking dependencies — you subscribe to them and route data through your networking solution (NGO, Mirror, Photon, etc.).

Architecture: Enemy Masses fires events → Your integration layer listens → You send RPCs. This keeps Enemy Masses network-agnostic while giving you everything needed for multiplayer.

5.1 Available Events on EnemyMassesCrowdController

5.2 Available Events on EnemyMassesRTSController

5.3 Event Data Structures (Already Defined)

All event data structures are in Arawn.EnemyMasses.Runtime.Networking:

5.4 Example: Subscribing in Your Network Authority

5.5 CrowdAgent Network Properties

Each CrowdAgent has built-in network support properties:

No modification to Enemy Masses source code is required! Just subscribe to the events and implement your RPC layer.


Step 6: Scene Setup

6.1 Required Scene Objects

Your multiplayer scene needs:

6.2 Prefab Configuration

NetworkManager:

  • Ensure NetworkManager component is present

  • Add UnityTransport component

  • Register EnemyMassesNetworkAuthority prefab

EnemyMassesNetworkAuthority Prefab:

  • Has NetworkObject component

  • Has EnemyMassesNetworkAuthority script

  • References are set in inspector (or found at runtime)


Step 7: Testing

7.1 Local Testing (Same PC)

  1. Build the game (File → Build Settings → Build)

  2. Run the built executable

  3. In Unity Editor, enter Play Mode

  4. In the built game: click Host

  5. In the Editor: enter the IP shown in the built game, click Join

7.2 LAN Testing (Two PCs)

  1. Build and run on both PCs

  2. On PC1: click Host, note the IP address shown

  3. On PC2: enter PC1's IP address, click Join

7.3 Internet Testing

For internet play, you have options:

Option A: Port Forwarding

  • Host forwards port 7777 on their router

  • Clients use host's public IP

Option B: Unity Relay Service


Step 8: Common Issues & Solutions

Issue: "Connection Failed"

Causes:

  • Firewall blocking port 7777

  • Wrong IP address

  • Host not actually hosting

Solutions:

  • Add firewall exception for your game

  • Ensure host clicked "Host" first

  • Use 127.0.0.1 for same-PC testing

Issue: Agents Not Syncing

Causes:

  • Network IDs not assigned

  • Events not subscribed

  • Wrong authority check

Solutions:

  • Ensure RegisterAgent() is called for all spawned agents

  • Check event subscriptions in OnNetworkSpawn()

  • Verify IsServer checks are correct

Issue: Commands Rejected

Causes:

  • Agent ownership not set

  • Anti-cheat validation failing

  • Server RPC rejected due to ownership requirements

Solutions:

  • Set agent.ownerPlayerId when spawning

  • Check ValidateMoveCommand() logs

  • Temporarily disable anti-cheat for debugging

  • Ensure Server RPCs use RequireOwnership = false when clients don't own the authority object

Issue: High Latency / Lag

Solutions:

  • Reduce stateSyncInterval (more frequent updates)

  • Implement client-side prediction

  • Use interpolation for smooth visuals


Step 9: Production Considerations

Security

  • Never trust client data blindly

  • Validate all commands on server

  • Rate-limit requests to prevent spam

  • Consider encryption for sensitive data

Scalability

  • For 500+ agents, batch state updates

  • Use delta compression (only send changes)

  • Consider interest management (don't sync distant agents)

Reliability

  • Handle disconnections gracefully

  • Implement reconnection logic

  • Save game state for host migration (advanced)


Complete File Structure


Late-Joiner Synchronization

When a player joins mid-game, they need the full current state of all agents. The SendFullStateToClient() method handles this automatically.

How It Works

  1. Player ConnectsOnClientConnected fires on the server

  2. Server Waits → Short delay (0.5s) ensures client is ready

  3. Full State Sent → Server sends AgentFullState for every agent (batched)

  4. Client Applies → Client spawns missing agents and updates existing ones

What Gets Synced

Property
Description

networkId

Unique identifier for the agent

factionIndex

Which faction the agent belongs to

position

World position (Vector3)

rotation

World rotation (Quaternion)

health

Current health value

isDead

Whether agent is dead

ownerPlayerId

Which player owns this agent

Custom Late-Joiner Logic

You can extend the late-joiner sync to include custom data:


Unity's NavMeshAgent is not deterministic across different machines. Even with identical inputs, agents will drift apart over time due to:

  • Floating-point differences between CPUs

  • Frame rate variations

  • Path recalculation timing

  • Local avoidance interactions

When is "Deterministic Enough" Actually Enough?

In some scenarios, running AI locally on each client without position sync can work:

Scenario
Local AI Works?
Why

Simple target-chasing

Maybe

If enemies just move toward synced player positions, they'll be close enough

Hit validation with generous tolerance

Yes

If you allow 5+ meter tolerance, drift doesn't matter

Strict distance-based hit validation

No

Enemies could be meters apart between server/client

Visual-only (no gameplay impact)

Yes

Players won't notice slight differences

Bottom Line: If your server validates hits with distance/facing checks, you must sync positions from the server. Client-side AI simulations will NOT match closely enough.

The Solution: Server Authority

This guide uses server-authoritative positions:

  1. Server runs all NavMeshAgents and owns the "truth"

  2. Clients receive position updates periodically via SyncCriticalState()

  3. Correction threshold (1 meter by default) prevents jitter

Tuning Position Sync

Scenario

stateSyncInterval

Correction Threshold

Validation Tolerance

RTS (commands only)

0.5s

2.0f

N/A

Co-op casual

0.2s

1.0f

5.0f

Co-op precise

0.1s

0.5f

2.0f

Competitive

0.05s

0.25f

1.0f

Note: Lower intervals = more bandwidth but more accurate positions.

Alternative: Client-Side Prediction

For smoother visuals, you can implement client-side prediction:


Next Steps

  1. Add More Features:

    • Chat system

    • Player names/colors

    • Victory/defeat conditions

  2. Polish:

    • Connection timeout handling

    • Loading screens

    • Error messages

  3. Advanced:

    • Unity Relay for NAT traversal

    • Lobby system with Unity Lobby

    • Matchmaking


Last updated