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.
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
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 successfullyin the server console
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:
Or download from adoptium.net — choose JDK 21 (LTS). Verify with:
Should show version 21.
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\binto your System PATH
(Control Panel → System → Advanced → Environment Variables)
Verify with:
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.
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 name | Type | Is Array |
|---|---|---|
message | string | No |
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 name | Type | Is Array |
|---|---|---|
playerId | int | No |
playerName | string | No |
message | string | No |
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.
Configure Project Settings#
Click Settings in the top bar, then select Project Settings in the sidebar. Set:
| Field | Value |
|---|---|
| Namespace | ChatRoom |
| Overwrite Files | ✓ |
| SFS3 Lib Path | C:\Users\YourName\SmartFoxServer3\server\lib |
| SFS3 Extensions Path | C:\Users\YourName\SmartFoxServer3\server\extensions |
Changes are saved automatically — no Save button needed.
Configure SmartFox Settings#
Click Settings in the top bar, then select SmartFox Settings in the sidebar. Set:
| Field | Value |
|---|---|
| Host | 127.0.0.1 |
| Zone | ChatRoomZone |
| Extension Name | ChatRoom |
| Extension Class | com.mycompany.chatroom.ChatRoomExtension |
| TCP Port | 9977 |
| WS Port | 9977 |
Click Save Settings at the bottom of the panel after making any changes.
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 serverChatReceivedEvent.cs— receives a broadcast from the serverSmartFoxConnectionManager.cs— handles the SFS3 connection lifecycle
Open SendChatRequest.cs and you'll see something like:
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:
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;
}
}
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 classhandlers/HandleSendChat.java— reads the message and asks the extension to broadcast itpom.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:
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.
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:
C:\Users\YourName\SmartFoxServer3\server\zones\
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
ConnectionManagerGameObject - A
NetCrafterEventDispatcherGameObject - A
NetworkManagerGameObject - A
Main Camera - A
SpawnPointshierarchy
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:
| Property | Type | Description |
|---|---|---|
IsConnected | bool | True when connected to SFS3. |
LocalUserId | int | The local player's SFS3 user ID. Returns -1 if not logged in. |
LocalUserName | string | The 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).
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
- In the Hierarchy, right-click → UI → Canvas
- On the Canvas component, set Render Mode to Screen Space - Overlay
- Right-click the Canvas → UI → Panel — name it
ChatBackground
8.2Create the Login Panel
- Right-click
ChatBackground→ UI → Panel — name itLoginPanel - Stretch to fill the parent (anchor: stretch/stretch)
- Right-click
LoginPanel→ UI → Text - TextMeshPro- Name it
TitleText - Set text to
Chat Room Login - Center align, increase font size to 36
- Name it
- Right-click
LoginPanel→ UI → Input Field - TextMeshPro- Name it
UsernameInput - Set Placeholder text to
Enter your name... - Position below
TitleText
- Name it
- Right-click
LoginPanel→ UI → Button - TextMeshPro- Name it
LoginButton - Set button label text to
Login - Position below
UsernameInput
- Name it
8.3Create the Chat Panel
- Right-click
ChatBackground→ UI → Panel — name itChatPanel - Stretch to fill the parent
- Right-click
ChatPanel→ UI → Scroll View — name itChatScrollView- Uncheck Horizontal scroll
- Keep Vertical scroll checked
- Resize to fill the top ~80% of
ChatPanel
- Expand
ChatScrollViewin 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
- Right-click Content → UI → Text - TextMeshPro — name it
- Right-click
ChatPanel→ UI → Input Field - TextMeshPro- Name it
MessageInput - Placeholder:
Type a message... - Position at the bottom of
ChatPanel
- Name it
- Right-click
ChatPanel→ UI → Button - TextMeshPro- Name it
SendButton - Label:
Send - Position to the right of
MessageInput
- Name it
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:
- Go to Edit → Project Settings → Player
- Find Scripting Define Symbols (under Other Settings)
- Add:
NETCRAFTER_CHAT_EXAMPLE - 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.
- Attach the
ChatUIscript to the ChatBackground Panel - In the Inspector wire up all fields:
- Login Panel: drag
LoginPanel - Username Input: drag
UsernameInput - Chat Panel: drag
ChatPanel - Chat Log: drag
ChatLog(insideViewport/Content) - Message Input: drag
MessageInput - Scroll Rect: drag
ChatScrollView
- Login Panel: drag
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
- Select
LoginButtonin the Hierarchy - In the Inspector, find the Button component → On Click ()
- Click + to add a new event
- Drag
ChatBackground(with ChatUI attached) into the object slot - From the function dropdown select ChatUI → OnLoginClicked()
8.6Wire the Send button
- Select
SendButtonin the Hierarchy - In the Inspector, find the Button component → On Click ()
- Click + to add a new event
- Drag
ChatBackgroundinto the object slot - From the function dropdown select ChatUI → SendMessage()
8.7Wire the ChatReceived Scene Event
- Go to Tools → Pivec Labs in the menu bar
- Navigate to Scenes → Scene Events
- Confirm the Router is present in the scene (green ✓). If not, click Add Router to Scene
- Find
ChatReceivedin the list - Click + under On Received
- Drag
ChatBackgroundinto the object slot - From the function dropdown select ChatUI → OnChatBroadcastReceived()
8.8How it works
- On Play:
LoginPanelis shown,ChatPanelis 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
ConnectionManagerdispatches aRoomJoinedevent viaNetCrafterEventDispatcher ChatUIreceives it (subscribed inStart()), hidesLoginPanel, showsChatPanel- 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.
Start SFS3 and Test#
9.1Start SmartFoxServer 3
Run SmartFoxServer3.exe (or the service). Check the log for:
{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.
What's Next#
You've got a working real-time chat room. Here's where to go from here.