This is the official SmartFoxServer 3 API for C++ that can be used to build multiplayer clients for games and applications, including those made with a C++ game engine. The C++ API is derived from the reference API implementation in Java and supports all of its features.
The API is distributed as source code: you build it on your own system, with your own compiler and settings, and link the resulting static library into your project.
There are no external libraries to install. Every dependency is included in the sources under
src/third_party/, and is built together with the API.
The API is built with CMake. From the root of this package, configure an out-of-source build directory and build it:
mkdir build
cd build
cmake ../src
cmake --build . --parallel 8
The --parallel option builds with more than one job at a time, which is much faster.
Set the number to the count of CPU cores of your machine. Without this option the build uses
one job only.
The build type is Release if you do not set one. For a debug build pass
-DCMAKE_BUILD_TYPE=Debug to the first command.
The same four steps apply, but the last one must name the build type. Visual Studio keeps
all build types in one project, so it ignores -DCMAKE_BUILD_TYPE and you select
the type at build time with --config:
mkdir build
cd build
cmake ../src
cmake --build . --config Release --parallel 8
Without --config Visual Studio makes a Debug build. Use
--config Debug if that is what you want.
The configure step also writes a Visual Studio solution,
build/SFS3_CppClient.sln. You can open it in the IDE and build from there instead
of the command line. It is a normal CMake product: you do not have to use it, and you must
not edit it by hand, because the next cmake ../src writes it again.
The build makes three static libraries:
sfs_client — the API itselfsfs_compression — the bundled compression codesfs_mbedtls — the bundled TLS code, for HTTPSOn macOS and Linux they are in the build/ folder, with the names
libsfs_client.a, libsfs_compression.a and libsfs_mbedtls.a.
On Windows with Visual Studio each build type has its own folder, so the files are in
build/Release/ (or build/Debug/), with the names
sfs_client.lib, sfs_compression.lib and sfs_mbedtls.lib.
The .pdb files next to them hold the debug symbols.
Link all three into your application.
Add src/ and src/third_party/ to your include paths. On macOS and iOS you
must also link the CoreFoundation and Security system frameworks. On Windows
with MinGW you must link ws2_32 and mswsock; with Visual Studio these come in
on their own.
If your project uses CMake too, there is a simpler way. Add this package to your build and link the target: CMake then applies the include paths and the system libraries for you.
add_subdirectory(path/to/SFS3_API_Cpp/src sfs3)
target_link_libraries(myGame PRIVATE sfs_client)
You can find the generated documentation under the Docs/ folder, or you can consult the latest API doc on our website: docs3.smartfoxserver.com
For questions and issue reports use the official SmartFoxServer 3 support forums: https://smartfoxserver.support/viewforum.php?f=37
This connects to a SmartFoxServer 3 instance on the same machine and, once the connection is up,
logs in the Playground Zone. Every standard installation has that Zone, and the defaults of
ConfigData already point at a local server on port 9977, so nothing has to be configured.
#include "ConfigData.h"
#include "SmartFox.h"
#include "event/SFSEvent.h"
#include "log/Log.h"
#include "requests/LoginRequest.h"
using namespace sfs3;
int main()
{
SmartFox sfs;
ConfigData cfg; // host 127.0.0.1, port 9977, zone "Playground"
sfs.addEventListener(event::SFSEvent::CONNECTION, [&sfs, &cfg](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, discard it and build a new one.
log::warn("Connection failed: {}", evt.errMessage.value_or("unknown reason"));
return;
}
log::info("Connected to {}:{}", cfg.host, cfg.port);
sfs.send(requests::LoginRequest { "myUserName" });
});
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.
log::info("Logged in zone {} as {}", evt.zoneName, evt.mySelf->getName());
});
sfs.addEventListener(event::SFSEvent::LOGIN_ERROR, [](const event::ApiEvent& e)
{
auto& evt = static_cast<const event::LoginError&>(e);
log::error("Login failed ({}): {}", evt.errorCode, evt.errorMessage);
});
// Start the connection. It is made in the background, so a failed Result here
// means the settings are not valid, not that the server refused the connection.
auto res = sfs.connect(cfg);
if (!res.ok)
{
log::error("Cannot start the connection: {}", res.error.value_or("unknown reason"));
return 1;
}
bool running = true;
while (running)
{
// Events are queued, so the loop must drain them. This is where the
// handlers above are called.
sfs.processEvents();
// ... the rest of the game loop
}
}
Events are queued by default, so the game loop must drain them with processEvents():
that is where the handlers above are called, on the thread that calls it. This is what a game engine
needs, since game objects can only be touched from the main thread.