Unreal Engine 5 C++ Plugin
This guide shows how to add the SmartFoxServer 3 C++ API to an Unreal Engine 5 project, on Windows and on macOS. It covers the tools you need, how to add the plugin to a project, how to build it, and how to test the connection with a simple Actor.
The plugin is in the Unreal/SFS3Client/ folder of this package. It holds the full API as source code, plus a thin Unreal layer:
- USFS3Subsystem — keeps the SmartFox instance and calls its event handlers on the game thread, once per frame.
- Output Log — the API messages go to the Output Log, in the LogSFS3 category.
- HTTP — HTTP calls (BlueBox, encryption setup) use the engine's own HTTP module.
beta
The C++ API and the plugin are in beta. Please report any problem on our support forum (see the end of this page).
Prerequisites
All platforms
- A C++ project. A Blueprint-only project has no C++ code to call the API from. To convert it, add any C++ class: in the editor, select Tools → New C++ Class.
- A SmartFoxServer 3 instance that the machine can reach. For the first test, a server on the same machine is the simplest.
Windows
- Unreal Engine 5.7+, installed from the Epic Games Launcher.
- Visual Studio 2022 or 2026, with the Game development with C++ workload.
- .NET Framework 4.8 SDK. UE 5.7 needs it to build. In the Visual Studio Installer, select Modify → Individual components and search for “.NET Framework 4.8 SDK”.
Epic lists the exact Visual Studio versions and components for each engine version: Setting up Visual Studio for Unreal Engine.
macOS
- Unreal Engine 5.7+, installed from the Epic Games Launcher.
- macOS 14 Sonoma or later.
- Xcode 15.2 or later. We tested with macOS 15 Sequoia and Xcode 16.4.
- Start Xcode once before the first build, to accept the license and install its extra components.
Xcode
Each engine version supports a range of Xcode versions. A newer Xcode can break the build of an older engine, so check Epic```s table before you update Xcode: macOS Development Requirements for Unreal Engine.
Other supported UE versions
We have tested the plugin on Unreal Engine 5.7 (both macOS and Windows) but we expect it to work on prior versions as well (5.3 — 5.6). In case you are having issues with other versions, please send us all the details via our support forum
Add the plugin to a project
In the steps below, MyGame is the name of your project. Replace it with the real name.
- Close the Unreal Editor.
-
Copy the plugin. In the root folder of your project (where MyGame.uproject is), make a Plugins folder if there is none. Copy the ``Unreal/SFS3Client``` folder of this package into it. The result must be:
-
Add the dependency. Open Source/MyGame/MyGame.Build.cs and add "SFS3Client" to the list of modules that your game uses:
Your list can have other modules. Only addPublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "SFS3Client" });"SFS3Client"to it. -
Build the project, as the next section shows.
A plugin in the project's Plugins folder is enabled by default. To check it, select Edit → Plugins in the editor and search for “SmartFoxServer”.
Note
Your game module needs no other settings. The plugin compiles the API with the options it needs (for example, exceptions inside the API), and the API headers compile with the engine```s defaults.
Build the project
From the editor
Double-click MyGame.uproject. The editor sees that modules are missing and asks you to rebuild them. Click Yes. The first build compiles the whole API, so it takes a few minutes.
If the build fails, the editor only tells you that it failed. To see the errors, build from the command line.
From the command line
The target name is the project name plus Editor. It comes from the file Source/MyGameEditor.Target.cs.
Windows (Command Prompt):
"C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\Build.bat" ^
MyGameEditor Win64 Development -Project="C:\Path\To\MyGame\MyGame.uproject"
macOS (Terminal):
"/Users/Shared/Epic Games/UE_5.7/Engine/Build/BatchFiles/Mac/Build.sh" \
MyGameEditor Mac Development -Project="/path/to/MyGame/MyGame.uproject"
Change the engine path if you installed the engine in a different folder. When there are many errors, look at the first one. It is usually the real cause, and the others follow from it.
The build tool also writes a full log of each build:
- Windows:
%LOCALAPPDATA%\UnrealBuildTool\Log.txt - macOS:
~/Library/Application Support/Epic/UnrealBuildTool/Log.txt
From Visual Studio or Xcode
When the editor is open, select Tools → Refresh Visual Studio Project (on macOS: Refresh Xcode Project), then Tools → Open Visual Studio (Open Xcode). Build the Development Editor configuration. Close the editor first, if you changed a header file.
Test the connection
This Actor connects to the server when you press Play, logs in, and writes each step to the Output Log. The connection settings are properties, so you can change them in the editor without a new build.
1. Create the Actor class
In the editor, select Tools → New C++ Class, select Actor as the parent class, and enter the name SFS3TestActor. The editor adds SFS3TestActor.h and SFS3TestActor.cpp to your project. Replace their contents with the code below.
SFS3TestActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SFS3TestActor.generated.h"
/**
* Connects to SmartFoxServer 3 and logs in when Play starts.
* Each step is written to the Output Log.
*/
UCLASS()
class MYGAME_API ASFS3TestActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "SFS3")
FString Host = TEXT("127.0.0.1");
UPROPERTY(EditAnywhere, Category = "SFS3")
int32 Port = 9977;
UPROPERTY(EditAnywhere, Category = "SFS3")
FString Zone = TEXT("Playground");
// Leave it empty to log in as a guest: the server then assigns a name
UPROPERTY(EditAnywhere, Category = "SFS3")
FString UserName;
// Needs a server with encryption turned on and a valid certificate
UPROPERTY(EditAnywhere, Category = "SFS3")
bool bUseSSL = false;
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};
MYGAME_API with the macro of your module: the project name in capital letters, plus _API. The file that the editor made already has the correct on
SFS3TestActor.cpp
#include "SFS3TestActor.h"
#include "Engine/GameInstance.h"
#include "SFS3Subsystem.h"
#include "ConfigData.h"
#include "SmartFox.h"
#include "event/SFSEvent.h"
#include "requests/LoginRequest.h"
#include <memory>
#include <string>
using namespace sfs3;
void ASFS3TestActor::BeginPlay()
{
Super::BeginPlay();
// The subsystem owns the SmartFox instance and calls the event handlers on the game thread
USFS3Subsystem* Sfs3 = GetGameInstance()->GetSubsystem<USFS3Subsystem>();
Sfs3->SetSmartFox(std::make_unique<SmartFox>());
SmartFox* Sfs = Sfs3->GetSmartFox();
const std::string Name = TCHAR_TO_UTF8(*UserName);
Sfs->addEventListener(event::SFSEvent::CONNECTION, [Sfs, Name](const event::ApiEvent& E)
{
auto& Evt = static_cast<const event::Connection&>(E);
if (!Evt.success)
{
// The server is unreachable. This SmartFox instance is now spent:
// to try again, create a new one and pass it to SetSmartFox().
UE_LOG(LogTemp, Warning, TEXT("SFS3 test: connection failed: %s"),
UTF8_TO_TCHAR(Evt.errMessage.value_or("unknown reason").c_str()));
return;
}
UE_LOG(LogTemp, Display, TEXT("SFS3 test: connected, logging in..."));
Result Res = Sfs->send(requests::LoginRequest { Name });
if (!Res.ok)
UE_LOG(LogTemp, Error, TEXT("SFS3 test: cannot send the login: %s"),
UTF8_TO_TCHAR(Res.error.value_or("unknown reason").c_str()));
});
Sfs->addEventListener(event::SFSEvent::CONNECTION_LOST, [](const event::ApiEvent& E)
{
auto& Evt = static_cast<const event::ConnectionLost&>(E);
UE_LOG(LogTemp, Warning, TEXT("SFS3 test: connection lost, reason: %s"),
UTF8_TO_TCHAR(Evt.disconnectionReason.c_str()));
});
Sfs->addEventListener(event::SFSEvent::LOGIN, [](const event::ApiEvent& E)
{
auto& Evt = static_cast<const event::Login&>(E);
// The server has the last word on the name: it can change the one you
// asked for, and a Zone with a guest system assigns one on its own.
UE_LOG(LogTemp, Display, TEXT("SFS3 test: logged in zone %s as %s"),
UTF8_TO_TCHAR(Evt.zoneName.c_str()), UTF8_TO_TCHAR(Evt.mySelf->getName().c_str()));
});
Sfs->addEventListener(event::SFSEvent::LOGIN_ERROR, [](const event::ApiEvent& E)
{
auto& Evt = static_cast<const event::LoginError&>(E);
UE_LOG(LogTemp, Warning, TEXT("SFS3 test: login failed (%d): %s"),
(int)Evt.errorCode, UTF8_TO_TCHAR(Evt.errorMessage.c_str()));
});
ConfigData Cfg;
Cfg.host = TCHAR_TO_UTF8(*Host);
Cfg.port = Port;
Cfg.zone = TCHAR_TO_UTF8(*Zone);
Cfg.useSSL = bUseSSL;
UE_LOG(LogTemp, Display, TEXT("SFS3 test: API %s, connecting to %s:%d"),
UTF8_TO_TCHAR(Sfs->getVersion().c_str()), *Host, Port);
// The connection is made in the background, so a failed Result here means
// the settings are not valid, not that the server refused the connection.
Result Res = Sfs->connect(Cfg);
if (!Res.ok)
UE_LOG(LogTemp, Error, TEXT("SFS3 test: cannot start the connection: %s"),
UTF8_TO_TCHAR(Res.error.value_or("unknown reason").c_str()));
}
void ASFS3TestActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// One SmartFox instance per session: drop it when Play stops
if (USFS3Subsystem* Sfs3 = GetGameInstance()->GetSubsystem<USFS3Subsystem>())
{
if (SmartFox* Sfs = Sfs3->GetSmartFox(); Sfs && Sfs->isConnected())
Sfs->disconnect();
Sfs3->SetSmartFox(nullptr);
}
Super::EndPlay(EndPlayReason);
}
2. Build
Close the editor and build the project, as shown in Build the project. A new class with properties needs a full build: Live Coding (the editor's quick rebuild) does not always pick up changes to a header file.
3. Add the Actor to the level
- Open the project in the editor.
- In the Content Browser, open C++ Classes → MyGame and find SFS3TestActor. You can also search for it in the Place Actors panel.
- Drag it into the level.
- With the Actor selected, set the connection settings in the Details panel, in the SFS3 section.
- Save the level.
4. Run the test Select Window → Output Log, then press Play. Type "SFS3" in the search field of the Output Log, to see only the lines of the test and of the API. A successful test shows:
LogTemp: Display: SFS3 test: API 3.1.0_beta, connecting to 127.0.0.1:9977
LogTemp: Display: SFS3 test: connected, logging in...
LogTemp: Display: SFS3 test: logged in zone Playground as Guest#1
The version and the user name can be different. When you stop Play, the Actor disconnects from the server.
5. Test a packaged game (optional) Select Platforms → Windows (or Mac) → Package Project. The plugin needs no extra settings: it is compiled into the game. A packaged game writes its log to a file:
- Windows:
<packaged folder>\MyGame\Saved\Logs\MyGame.log - macOS:
~/Library/Logs/MyGame/MyGame.log
To see the log while the game runs, start it with the -log option. On macOS, start the program inside the app bundle from the Terminal, and the log shows there:
Using the API in your game
The subsystem
USFS3Subsystem is a Game Instance Subsystem, so it lives as long as the game instance and the connection stays open when the map changes. Get it from any Actor or component:
USFS3Subsystem* Sfs3 = GetGameInstance()->GetSubsystem<USFS3Subsystem>();
sfs3::SmartFox* Sfs = Sfs3->GetSmartFox();
- Events run on the game thread. Each frame the subsystem calls
SmartFox::processEvents(), and the event handlers run there. So a handler can safely use Actors and other engine objects. Do not callprocessEvents()yourself. - One SmartFox instance is one session: connect, play, disconnect. After a disconnection, create a new instance and pass it to
SetSmartFox(). The old instance is then destroyed. The subsystem never reconnects on its own. - Do not keep the pointer that
GetSmartFox()returns after the nextSetSmartFox()call. - If a handler uses your Actor (it captures
this), remove the SmartFox instance inEndPlay, as the test Actor does. Otherwise, the handler can run after the Actor is gone. - Call all the subsystem methods from the game thread.
Logs
The API writes its messages to the Output Log, in the LogSFS3 category. To see more or fewer messages, set the API's log level, for example at the start of the game:
#include "log/Log.h"
sfs3::log::setLevel(sfs3::log::Level::Debug); // Debug, Info, Warn, Error or Off
Known limitations
- Self-signed certificates.
ConfigData::allowUnsafeSSLhas no effect under Unreal: the engine's HTTP module cannot skip the certificate check for one request. The API writes a warning and ignores the setting. So encryption (useSSL) works only with a server that has a valid certificate. The default self-signed certificate of a local server is refused. - Untested platforms. Linux, iOS and Android are allowed by the plugin, but we did not test them yet.