C++ SmartFoxServer 3 API

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.

Prerequisites

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.

Building 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.

Windows with Visual Studio

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.

Where the libraries are

The build makes three static libraries:

On 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)

Documentation

You can find the generated documentation under the Docs/ folder, or you can consult the latest API doc on our website: docs3.smartfoxserver.com

Discussions and bug reports

For questions and issue reports use the official SmartFoxServer 3 support forums: https://smartfoxserver.support/viewforum.php?f=37

Example of usage

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.