Getting Started
Install to first generated file in under 10 minutes with NetCrafter Relay.
This guide walks you from a fresh Unity project to a running NetCrafter Relay schema that generates working co-op network code.
1. Install the asset#
Import the NetCrafter Relay .unitypackage into your project via Assets → Import Package → Custom Package. After import the following structure will appear under Assets/PivecLabs/NetCrafterRelay/:
Documentation/ — these docs
Editor/ — editor window and code-generation tools
Examples/ — example schemas
Plugins/x86_64/ — NetCrafterRelayNative.dll (Windows only)
Scripts/ — ScriptableObject definitions + the pure C# relay client
Templates/ — built-in code templates
2. Open the NetCrafter Relay editor window#
Unity menu → Tools → Pivec Labs → NetCrafter Relay
The window opens with four top-level sections in the toolbar: Definition, Code, Scenes, and Settings. Resize it to at least 1100 × 650 if controls appear clipped.
3. Create a schema (Definition)#
A Definition is a named collection of messages, events, models, and host actions. Each definition generates its own isolated set of C# files.
- In the schema bar at the very top of the window, click Create Definition.
- Name it (e.g.
MyGame). A folder is created atAssets/PivecLabs/NetCrafterRelay/Definitions/MyGame/.
You can switch between definitions using the dropdown in the schema bar.
4. Configure Relay settings#
Click Settings in the top toolbar, then Relay Settings in the sidebar.
| Field | Value to set |
|---|---|
| Relay Host | Your own coturn endpoint (required, no default — see Server Deployment Guide) |
| Relay Port | 3478 (TURN default) |
| Rendezvous URL | Your own rendezvous service (required, no default — see Server Deployment Guide) |
Leave all other fields at their defaults for now. Click Save Settings.
Allow HTTP in Player Settings — a fresh Unity project blocks this by default. The Rendezvous URL above is a plain http:// endpoint, not https://. Unity 2022+ projects default Player Settings → Other Settings → Configuration → Allow downloads over HTTP to Not allowed — with that default, the very first connection attempt throws InvalidOperationException: Insecure connection not allowed from UnityWebRequest.BeginWebRequest(), before any relay-specific code even runs. Set it to Always allowed now, before you test anything — Allowed in development builds is enough for Editor testing, but a real build for players needs Always allowed, so there's no reason to set it to anything less permanent. Confirmed live: a brand-new project with this left at its default fails this way on the very first host/join attempt, with every other setting correct.
5. Define a model (Data Definition)#
Models are plain data classes that can be nested inside messages and events. Create a PlayerState model:
- Click Definition in the top toolbar, then Data Definitions in the sidebar.
- Click Create Data Definition. Name it
PlayerState. - Add fields:
| Name | Type |
|---|---|
position |
Vector3 |
health |
int |
username |
string |
isAlive |
bool |
6. Define a message (ClientToHost)#
Messages sent from a client to the host generate a {Name}Request class with a Send() method.
- Click Definition → Messages in the sidebar.
- Click Create Gameplay Message. Name it
PlayerMove. - Set Direction to
ClientToHost. - Set Host Handler Name to
HandlePlayerMove(this must match a Host Action you add next). - Add fields:
| Name | Type |
|---|---|
playerId |
PlayerId |
position |
Vector3 |
speed |
float |
Add a matching Host Action
- Click Host Actions in the sidebar.
- Click Create Host Action. Set Handler Name to
HandlePlayerMove.
This logic runs only on whichever peer is currently hosting — there's no separate server process to deploy.
7. Define an event (HostToClient)#
Events sent from the host to a client generate a {Name}Event class with Serialize()/Deserialize() methods.
- Click Messages in the sidebar. Click Create Gameplay Message.
- Name it
PlayerStateUpdate. Set Direction toHostToClient. - Set Host Handler Name to
HandlePlayerStateUpdate. - Add a field: name
state, typestring.
Note: To use a nested model type such as PlayerState as a field type, open the generated .asset file in a text editor and set type: PlayerState directly. The editor field-type dropdown currently shows only primitive types.
Add a matching Host Action with handler name HandlePlayerStateUpdate.
8. Run Generate All Files#
- Click Code in the top toolbar, then Build Code in the sidebar.
- Click Validate Definition first to confirm there are no errors.
- Click Generate All Files.
Generated files appear in:
Assets/PivecLabs/NetCrafterRelay/Definitions/MyGame/Generated/Client/
Assets/PivecLabs/NetCrafterRelay/Definitions/MyGame/Generated/Host/
9. What was generated#
For the schema above, the generator produces:
| File | Description |
|---|---|
PlayerMoveRequest.cs |
Send a PlayerMove message to the host |
PlayerStateUpdateEvent.cs |
Receive a PlayerStateUpdate event from the host |
PlayerState.cs |
Serialisable data model |
RelayConnectionManager.cs |
MonoBehaviour that manages the relay connection (Client folder) |
HostActionRouter.cs |
Host-side logic — runs only when this peer is hosting (Host folder) |
10. Set up your scene#
The Scene Assistant automates the boilerplate of wiring a multiplayer scene.
Create the ConnectionManager prefab
- Click Scenes in the top toolbar, then Prefab Generator in the sidebar.
- Click Create ConnectionManager Prefab.
The generator finds the INetCrafterConnection-implementing type in your Generated/Client/ folder, wraps it in a prefab, and saves it to Prefabs/RelayConnectionManager.prefab — a sibling of Generated/, not inside it, since this prefab holds your actual relay host/rendezvous URL/TURN credentials once you configure it and is never touched by code regeneration. If the script is not found, generate client code first (step 8).
Create the scene
- Click Scene Assistant in the sidebar.
- Confirm the green banner shows that the prefab was found.
- Optionally enable Add Event Dispatcher — this adds a
NetCrafterEventDispatcherGameObject that lets any script subscribe to host events without touching the generated file (see step 13). - Click Create Multiplayer Scene.
The assistant creates a new scene containing:
- A Main Camera
- A ConnectionManager GameObject (instantiated from the prefab)
- An EventDispatcher GameObject (if the toggle was enabled)
- A NetworkManager empty GameObject (reserved for future use)
The scene is saved to Definitions/{Name}/GameScene.unity and opened in the editor. You can rename it using the Scene Name field before clicking Create.
Add to an existing scene
If you already have a scene open, click Add to Existing Scene instead. This adds only the ConnectionManager (and EventDispatcher) — it does not add a camera or NetworkManager. If a ConnectionManager is already present, a confirmation dialog appears before a second one is added.
11. Add the ConnectionManager to a scene manually#
If you prefer not to use the Scene Assistant:
- Create an empty GameObject in your scene. Name it
ConnectionManager. - Attach the generated
RelayConnectionManagercomponent to it. - Drag this schema's
RelaySettings.asset(Definitions/{Name}/RelaySettings.asset) onto its Connection Settings field — the same asset the Relay Settings tab edits. (Prefab Generator does this automatically; it's only a manual step here because you're skipping the prefab.)
The ConnectionManager is a singleton with DontDestroyOnLoad. Call Connect() from a script when the game starts — with no join code set, it hosts a new session; call UpdateHost(joinCode) first to join an existing one:
void Start()
{
// Hosting a new session:
RelayConnectionManager.Instance.Connect();
// Or, to join an existing session:
// RelayConnectionManager.Instance.UpdateHost("GAME-4892");
// RelayConnectionManager.Instance.Connect();
}
12. Send a request#
Populate the generated request class and call Send():
using PivecLabs.MyGame.Generated;
public class PlayerController : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var request = new PlayerMoveRequest
{
playerId = RelayConnectionManager.Instance.LocalUserId,
position = transform.position,
speed = 5.0f
};
request.Send(RelayConnectionManager.Instance, reliable: true);
}
}
}
13. Handle an event#
Option A — Fill in the generated stub. Open RelayConnectionManager.cs and complete the stub method:
private void OnPlayerStateUpdate(PlayerStateUpdateEvent ev)
{
// ev.state is a deserialized PlayerState object
Debug.Log($"Player at {ev.state.position}, health {ev.state.health}");
}
Option B — Subscribe via EventDispatcher. From any MonoBehaviour in the scene, subscribe to the event by command name. This approach does not require editing the generated file:
private void OnEnable()
{
NetCrafterEventDispatcher.Instance.Subscribe("PlayerStateUpdate", OnPlayerState);
}
private void OnDisable()
{
NetCrafterEventDispatcher.Instance.Unsubscribe("PlayerStateUpdate", OnPlayerState);
}
private void OnPlayerState(object payload)
{
var ev = (PlayerStateUpdateEvent)payload;
Debug.Log($"Player at {ev.state.position}, health {ev.state.health}");
}
Both options can be used simultaneously — the dispatcher fires first, then the stub. See the API Reference for full EventDispatcher details.
Next steps#
- See the Manual for a complete reference of every tab and setting.
- See the API Reference for the full generated class API.
- See the Player Hosting Guide for how join codes work for players.