by Pivec Labs
⏱ 45–60 min Tutorial

Building a Multiplayer Chat Room

A complete, hands-on walkthrough of NetCrafter — from schema definition to a working real-time chat room on SmartFoxServer 3.

Time: 45–60 minutes 9 parts C# & Java
🧩
NetCrafter

What it is: A Unity asset that generates all your multiplayer networking code from a visual schema editor. Define your protocol once — NetCrafter writes the C# client code, Java server code, and SmartFoxServer config for you.

How to get it
  • Purchase and download from the Unity Asset Store (search "NetCrafter")
  • In Unity: Window → Package Manager → My Assets → NetCrafter → Import
  • Verify installation: Tools → Pivec Labs → NetCrafter Pro should open the editor window
🖥
SmartFoxServer 3

What it is: The multiplayer server that handles connections between players. NetCrafter generates code that talks to it.

Why you need it: Without a running server, your Unity game has nowhere to connect to.

How to get it
  • Download the free Community Edition (up to 100 concurrent users) from smartfoxserver.com/download
  • Install and run SmartFoxServer3.exe
  • You'll know it's working when you see SFS3 started successfully in the server console
☕
JDK 21 (Java Development Kit)

What it is: The tool that compiles the Java server code NetCrafter generates. You don't need to write any Java — NetCrafter does that — but you need the compiler to turn it into a runnable file.

Why version 21 specifically: SmartFoxServer 3's own files are compiled with Java 21. Using an older version (like 17) causes a "bad class file" error.

How to get it (recommended — Eclipse Adoptium)

Open PowerShell and run:

winget install EclipseAdoptium.Temurin.21.JDK

Or download from adoptium.net — choose JDK 21 (LTS). Verify with:

java -version

Should show version 21.

📦
Maven

What it is: A build tool for Java — think of it like Unity's own build system, but for Java. It compiles the generated server code and packages it into a .jar file (Java's equivalent of a DLL) with one command.

You never interact with Maven directly — NetCrafter's "Build JAR" button runs it automatically.

How to get it
  • Download from maven.apache.org/download.cgi — choose the Binary zip archive
  • Extract to e.g. C:\Program Files\Maven\
  • Add C:\Program Files\Maven\bin to your System PATH
    (Control Panel → System → Advanced → Environment Variables)

Verify with:

mvn -version

Or follow the full install guide.

About the Examples folder

Assets/PivecLabs/Examples/Chat/ ships with a prebuilt reference scene (ChatDemoScene.unity) and finished scripts (ChatNetworkManager.cs, ChatUIManager.cs). These exist purely for comparison — to check your work against once you're done, or to peek at if you get stuck. They are not a shortcut to learning NetCrafter. Build everything below in your own project, from scratch, by following the steps — that's what actually teaches you the schema → client → server → scene pipeline.

PART 1

Create the Schema#

A schema is the single source of truth for your multiplayer protocol. Everything — client code, server code, and config — is generated from it.

1.1Open the NetCrafter editor window

Go to Tools → Pivec Labs → NetCrafter Pro.

1.2Create a new schema

Click the Create Definition button at the top of the window. A dialog will appear asking for a name — enter ChatRoom and click Create. NetCrafter creates a folder at Assets/PivecLabs/NetCrafter/Definitions/ChatRoom/ with all the required subfolders.

1.3Define the SendChat message (Client → Server)

Click Definition in the top bar, then select Messages in the sidebar. Click Create Gameplay Message and name it SendChat. Set direction to ClientToServer. Set Transport to ExtensionRequest. Set Server Handler Name to HandleSendChat. Add one field:

Field nameTypeIs Array
messagestringNo

1.4Define the ChatReceived event (Server → Client)

Still under Definition, select Messages in the sidebar. Click Create Gameplay Message and name it ChatReceived. Set direction to ServerToClient. Set Transport to ExtensionEvent. Add three fields:

Field nameTypeIs Array
playerIdintNo
playerNamestringNo
messagestringNo

Leave Server Handler Name blank — this is a ServerToClient event, so it isn't triggered by an incoming client request; it's sent out as the response to the SendChat server action defined next.

1.5Define the server action

Still under Definition, select Server Actions in the sidebar. Click Create Server Action and name it HandleSendChat. Set:

  • Handler Name: HandleSendChat
  • Trigger Message: SendChat
  • Response Event: ChatReceived
  • Logic Type: BroadcastAll

One server action is all this example needs: the Response Event field tells NetCrafter which ServerToClient event to send back, and Logic Type: BroadcastAll tells it to send that event to every user in the room — no custom Java code required, as you'll see in Part 5.

PART 2

Configure Project Settings#

Click Settings in the top bar, then select Project Settings in the sidebar. Set:

FieldValue
NamespaceChatRoom
Overwrite Files✓
SFS3 Lib PathC:\Users\YourName\SmartFoxServer3\server\lib
SFS3 Extensions PathC:\Users\YourName\SmartFoxServer3\server\extensions

Changes are saved automatically — no Save button needed.

PART 3

Configure SmartFox Settings#

Click Settings in the top bar, then select SmartFox Settings in the sidebar. Set:

FieldValue
Host127.0.0.1
ZoneChatRoomZone
Extension NameChatRoom
Extension Classcom.mycompany.chatroom.ChatRoomExtension
TCP Port9977
WS Port9977

Click Save Settings at the bottom of the panel after making any changes.

PART 4

Generate Client Code#

Click Code in the top bar, then select Build Code in the sidebar. Click Validate Definition first — you should see a success dialog. Then click Generate All Files.

NetCrafter generates the following files in Assets/PivecLabs/NetCrafter/Definitions/ChatRoom/Generated/Client/:

  • SendChatRequest.cs — sends the chat message to the server
  • ChatReceivedEvent.cs — receives a broadcast from the server
  • SmartFoxConnectionManager.cs — handles the SFS3 connection lifecycle

Open SendChatRequest.cs and you'll see something like:

C# · SendChatRequest.cs
public class SendChatRequest
{
    public string message;

    public void Send(SmartFox Sfs)
    {
        var obj = new SFSObject();
        obj.PutString("message", message);
        Sfs.Send(new ExtensionRequest("SendChat", obj));
    }
}

Open ChatReceivedEvent.cs and you'll see:

C# · ChatReceivedEvent.cs
public class ChatReceivedEvent
{
    public int playerId;
    public string playerName;
    public string message;

    public static ChatReceivedEvent FromSFSObject(ISFSObject data)
    {
        var ev = new ChatReceivedEvent();
        ev.playerId = data.GetInt("playerId").GetValueOrDefault(0);
        ev.playerName = data.GetString("playerName") ?? string.Empty;
        ev.message = data.GetString("message") ?? string.Empty;
        return ev;
    }
}
PART 5

Generate and Build Server Code#

Click Code in the top bar, then select Server Code in the sidebar. Click Generate Server Code to produce the Java files.

Then click Build JAR (mvn package) and wait for the Maven build to complete — you should see BUILD SUCCESS in the output log. Then click Deploy to SFS3.

The generated files in Generated/Server/ include:

  • ChatRoomExtension.java — the main extension class
  • handlers/HandleSendChat.java — reads the message and asks the extension to broadcast it
  • pom.xml — Maven build file

Building and deploying the JAR itself doesn't require a script — click Deploy to SFS3 in the NetCrafter window → Code → Server Code tab, and it runs Maven and copies the JAR for you.

Open HandleSendChat.java and you'll see the generated structure. Because this action's Logic Type is BroadcastAll with a Response Event set, NetCrafter already generates a complete, working handler — there is nothing to extend by hand:

Java · HandleSendChat.java
public class HandleSendChat extends BaseClientRequestHandler {

    @Override
    public void handleClientRequest(User sender, ISFSObject params, TransportType transport) {
        String message = params.getString("message");
        if (message == null || message.isEmpty()) return;

        ChatRoomExtension ext = (ChatRoomExtension) getParentExtension();
        Room room = ext.getRoomForUser(sender.getId());
        if (room == null) return;

        ext.broadcastChat(sender, room, message);
    }
}

The actual broadcast — building the ChatReceived response with playerId/playerName/message and sending it to every user in the room — lives in a generated broadcastChat(...) helper method on ChatRoomExtension.java itself, generated once per Response Event and reused by any handler that targets it. You don't need to open or edit it for this tutorial, but it's there to read if you're curious how BroadcastAll is implemented.

PART 6

Generate the Config#

Click Code in the top bar, then select Config Generator in the sidebar. Click Refresh Preview to see the generated zone config XML. Click Generate {zoneName}.zone.xml to write it to Generated/Config/.

NetCrafter names the config file after your zone (e.g. ChatRoomZone.zone.xml). SFS3 scans its zones\ folder for files matching *.zone.xml — any other filename is ignored.

Click Deploy Config to SFS3 to copy the file automatically, or copy it manually to your SFS3 installation's zones folder:

Path
C:\Users\YourName\SmartFoxServer3\server\zones\
PART 7

Set Up the Scene#

7.1Create the ConnectionManager prefab

Click Scenes in the top bar, then select Prefab Generator in the sidebar. Click Create ConnectionManager Prefab. A prefab appears at Generated/Prefabs/ConnectionManager.prefab.

7.2Create the multiplayer scene

Click Scenes in the top bar, then select Scene Assistant in the sidebar. Add one spawn point at position (0, 0, 0). Click Create Multiplayer Scene (first button) and name it ChatRoomScene. The scene opens automatically with:

If your scene already exists, use Add to Existing Scene (second button) instead — it adds the ConnectionManager and related GameObjects without replacing the scene.

  • A ConnectionManager GameObject
  • A NetCrafterEventDispatcher GameObject
  • A NetworkManager GameObject
  • A Main Camera
  • A SpawnPoints hierarchy

The ConnectionManager connects to SFS3 automatically when the scene starts. If you need to connect manually, call SmartFoxConnectionManager.Instance.Connect() from your own script.

SmartFoxConnectionManager exposes these read-only properties:

PropertyTypeDescription
IsConnectedboolTrue when connected to SFS3.
LocalUserIdintThe local player's SFS3 user ID. Returns -1 if not logged in.
LocalUserNamestringThe local player's username. Returns an empty string if not logged in.

7.3Wire the ChatReceived event

Click Scenes in the top bar, then select Scene Events in the sidebar. Click Add Router to Scene. You'll see ChatReceived listed with its fields. Wire the On Received UnityEvent to a method on your ChatUI component (see Part 8).

PART 8

Build the Chat UI#

TextMeshPro: TextMeshPro is built into Unity 6. Before using any TMP components, import TMP Essentials once: Window → TextMeshPro → Import TMP Essentials. Click Import when prompted.

8.1Create the Canvas

  1. In the Hierarchy, right-click → UI → Canvas
  2. On the Canvas component, set Render Mode to Screen Space - Overlay
  3. Right-click the Canvas → UI → Panel — name it ChatBackground

8.2Create the Login Panel

  1. Right-click ChatBackground → UI → Panel — name it LoginPanel
  2. Stretch to fill the parent (anchor: stretch/stretch)
  3. Right-click LoginPanel → UI → Text - TextMeshPro
    • Name it TitleText
    • Set text to Chat Room Login
    • Center align, increase font size to 36
  4. Right-click LoginPanel → UI → Input Field - TextMeshPro
    • Name it UsernameInput
    • Set Placeholder text to Enter your name...
    • Position below TitleText
  5. Right-click LoginPanel → UI → Button - TextMeshPro
    • Name it LoginButton
    • Set button label text to Login
    • Position below UsernameInput

8.3Create the Chat Panel

  1. Right-click ChatBackground → UI → Panel — name it ChatPanel
  2. Stretch to fill the parent
  3. Right-click ChatPanel → UI → Scroll View — name it ChatScrollView
    • Uncheck Horizontal scroll
    • Keep Vertical scroll checked
    • Resize to fill the top ~80% of ChatPanel
  4. Expand ChatScrollView in the Hierarchy: Viewport → Content
    • Right-click Content → UI → Text - TextMeshPro — name it ChatLog
    • Anchors: stretch horizontally (left=0, right=1), Left/Right offsets to 0
    • Add Content Size Fitter component: Vertical Fit → Preferred Size
  5. Right-click ChatPanel → UI → Input Field - TextMeshPro
    • Name it MessageInput
    • Placeholder: Type a message...
    • Position at the bottom of ChatPanel
  6. Right-click ChatPanel → UI → Button - TextMeshPro
    • Name it SendButton
    • Label: Send
    • Position to the right of MessageInput

8.4Attach the ChatUI script

The ChatUI.cs script is included in the NetCrafter package at:

Assets/PivecLabs/Examples/Chat/Scripts/ChatUI.cs

Attach it to your Panel GameObject — no copying needed.

About the compile guard

ChatUI.cs contains a compile guard at the top:

#if NETCRAFTER_CHAT_EXAMPLE
// ... script contents ...
#endif

This prevents compile errors in projects that don't have the ChatRoom generated code yet. To enable the script in your project:

  1. Go to Edit → Project Settings → Player
  2. Find Scripting Define Symbols (under Other Settings)
  3. Add: NETCRAFTER_CHAT_EXAMPLE
  4. Click Apply

Note: If you are following this tutorial from scratch in a fresh project, add this define before generating your client code so the script is ready when you need it.

  1. Attach the ChatUI script to the ChatBackground Panel
  2. In the Inspector wire up all fields:
    • Login Panel: drag LoginPanel
    • Username Input: drag UsernameInput
    • Chat Panel: drag ChatPanel
    • Chat Log: drag ChatLog (inside Viewport/Content)
    • Message Input: drag MessageInput
    • Scroll Rect: drag ChatScrollView

Note: The namespace in the using statement (ChatRoom above) must match the Namespace field in your Project Settings tab. Check the top of your generated ChatReceivedEvent.cs to confirm the exact namespace if you get a compile error.

8.5Wire the Login button

  1. Select LoginButton in the Hierarchy
  2. In the Inspector, find the Button component → On Click ()
  3. Click + to add a new event
  4. Drag ChatBackground (with ChatUI attached) into the object slot
  5. From the function dropdown select ChatUI → OnLoginClicked()

8.6Wire the Send button

  1. Select SendButton in the Hierarchy
  2. In the Inspector, find the Button component → On Click ()
  3. Click + to add a new event
  4. Drag ChatBackground into the object slot
  5. From the function dropdown select ChatUI → SendMessage()

8.7Wire the ChatReceived Scene Event

  1. Go to Tools → Pivec Labs in the menu bar
  2. Navigate to Scenes → Scene Events
  3. Confirm the Router is present in the scene (green ✓). If not, click Add Router to Scene
  4. Find ChatReceived in the list
  5. Click + under On Received
  6. Drag ChatBackground into the object slot
  7. From the function dropdown select ChatUI → OnChatBroadcastReceived()

8.8How it works

  • On Play: LoginPanel is shown, ChatPanel is hidden
  • Player enters their name and clicks Login
  • SmartFoxConnectionManager.Login(username) sends the login request to SFS3
  • SFS3 logs the player in with their chosen name and automatically joins ChatRoom
  • On successful room join, the ConnectionManager dispatches a RoomJoined event via NetCrafterEventDispatcher
  • ChatUI receives it (subscribed in Start()), hides LoginPanel, shows ChatPanel
  • Player can now send and receive named messages

NetCrafterEventDispatcher uses DontDestroyOnLoad and is instantiated in Awake(), so its Instance is available by the time ChatUI.Start() runs.

PART 9

Start SFS3 and Test#

9.1Start SmartFoxServer 3

Run SmartFoxServer3.exe (or the service). Check the log for:

Log output
{ChatRoom}: ChatRoomExtension initialized

If you see this, the extension loaded correctly.

9.2Press Play in Unity

The ConnectionManager connects to 127.0.0.1:9977 automatically. Check the Unity console for a successful login message. After login, the ConnectionManager automatically joins ChatRoom — you should see a second console message confirming the room join.

Note: If the room join fails, make sure your zone config contains a room named ChatRoom. The Config Generator creates this room by default.

9.3Test with two instances

Build the project or open a second Unity Editor instance. Connect both. Type a message in one — it should appear in both chat logs within milliseconds.


NEXT

What's Next#

You've got a working real-time chat room. Here's where to go from here.

Sync player state

Add a PlayerState model to track player positions and use the Sync Rule Runtime to synchronise transforms automatically.

Customise generated code

Use the Template Override System to customise the generated SendChatRequest class without losing your changes on regeneration.

Add more rooms

Extend the zone config file with additional <Room> elements to support more than one chat room.

Explore a full example

See the Racing Tutorial or Character Tutorial for a more complete multiplayer setup with player movement and state sync.