TeamSpeak 3 SDK Client Intro

Hello World

Introduction

This article will guide you through creating your first TeamSpeak SDK client. This Hello World application will be able to connect to a TeamSpeak SDK server.

Overview of the TeamSpeak SDK architecture

The API of the TeamSpeak SDK is written in the C programming language. This allows you to integrate the SDK into various other programming languages including interpreted or bytecode-compiled languages, which all allow interaction with external C API libraries.

The TeamSpeak SDK API includes functions that can be called by your application and callback functions existing inside your code which can be called by the TeamSpeak libraries.

Functions you can call from within your application are prefixed with ts3client_### or ts3server_###: ts3client_initClientLib, ts3client_openPlaybackDevice, ts3client_startConnection. These functions can be actively used by your application to trigger TeamSpeak actions such as connecting to a server, switching to another channel, creating new channels etc.

Callbacks have a naming convention on###Event, e.g. onNewChannelEvent, onClientMoveEvent. Those events are passively called by the TeamSpeak library as notifications to your code when certain TeamSpeak actions have happened. For example, when a new channel has been created, the onNewChannelEvent would be called. When a client moves to another channel, onClientMoveEvent is called.

See the header clientlib.h for a list of all callbacks and functions. In addition, the included API reference documentation will describe each function and callback in detail.

Let's code

There're two lifespans of interest here: The lifespan of the client library, and within that the connection to the server.
This leads us to four topics of interest:

  • Initialization of the client library
  • Connecting to a TeamSpeak SDK server
  • Disconnecting from a server
  • Shutting down the client library

In addition to these, we'll also handle our first event and the lifespan of audio.

Initialization

Let's have a look at the client library's initialization function.

unsigned int ts3client_initClientLib(const struct ClientUIFunctions* functionPointers, const struct ClientUIFunctionsRare* functionRarePointers, int usedLogTypes, const char* logFileFolder, const char* resourcesFolder);`

Ignoring the other parameters in this example, let's focus on the function pointers and the resources folder.

Let's create a function that encapsulates the initialization process. We add the path to the sound backends as a parameter: void ts_init(const char* soundbackends_path).

The client library's initialization takes a ClientUIFunctions instance filled with the callback functions you want to use.
Let's start by creating the struct: struct ClientUIFunctions funcs; and initializing all callbacks with NULL: memset(&funcs, 0, sizeof(struct ClientUIFunctions));. It is sufficient to only assign the callback functions you are using. When adding more callbacks, add the function pointers here.
In our first client we're going to use a single callback.
We'll add a function with the corresponding signature

void onConnectStatusChangeEvent(uint64 connection_id, int new_status, unsigned int connection_error)

and add it to the struct: funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;

Now we're ready to initialize the TeamSpeak client library:

ts3client_initClientLib(&funcs, NULL, LogType_CONSOLE, NULL, soundbackends_path);

Identity

Now that we've covered the client library initialization, we'll need an identity to connect to a server. Each user connecting to a TeamSpeak Server is identified by a unique client identity. This identity can be reused for further connections to any TeamSpeak SDK server. For our Hello World use case we decide that it's just fine having one identity with an equal life-span as the library itself. Hence, let's finish our initialization function with creating an identity:
We add char* identity = NULL; on the file level and add the function call:
ts3client_createIdentity(&identity);
In your real application you should do this only once, store the identity and then reuse it.

An application might associate a TeamSpeak identity with a user account of that application. For example in a game with multiple characters per account, the developers could decide to either associate a single TeamSpeak identity with the game account, or even one TeamSpeak identity with each character.

Summing up initialization

That's it, we've finished the initialization code:

void ts_init(const char* soundbackends_path)
{
    /* Create struct for callback function pointers */
    struct ClientUIFunctions funcs;

    /* Initialize all callbacks with NULL */
    memset(&funcs, 0, sizeof(struct ClientUIFunctions));

    /* Callback function pointers */
    /* It is sufficient to only assign those callback functions you are using. When adding more callbacks, add those function pointers here. */
    funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;

    ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE, NULL, soundbackends_path);

    /* Create a new client identity */
    ts3client_createIdentity(&identity);
}

Please note, for readability, we've only included error checking in the full example source code to increase readability.

Connecting

Now we're ready to connect to a TeamSpeak SDK server. The connect function has to include the TeamSpeak client identity as created above, the server address and port, a requested client nickname and optionally which channel to join to, a server and channel password:

unsigned int ts3client_startConnection(
    uint64 serverConnectionHandlerID,
    const char* identity,
    const char* ip,
    unsigned int port,
    const char* nickname,
    const char** defaultChannelArray,
    const char* defaultChannelPassword,
    const char* serverPassword);

Spawning a server connection handler

With TeamSpeak, you can connect to multiple servers simultaneously. Each connection is identified by a server connection handler ID.

New server connection handlers are created with:

unsigned int ts3client_spawnNewServerConnectionHandler(int port, uint64* result);

The new server connection handler ID has to be stored and used as parameter in all further function calls, to identify which server connection should be addressed. At least one server connection handler has to be spawned. Hence, let's encapsulate the connection in a function, and let it return the connection id.

Summing up connecting

We now have all we need to connect. Let's see how it looks:

uint64 ts_connect()
{
    uint64 connection_id = 0;
    /* Spawn a new server connection handler using the default port and store the server ID */
    ts3client_spawnNewServerConnectionHandler(0, &connection_id);

    /* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
    ts3client_startConnection(connection_id, identity, "localhost", 9987, "client", NULL, "", "secret");

    return connection_id;
}

Disconnecting

When the client has disconnected from a server, the server connection handler can either be reused for a new connection via ts3client_startConnection or destroyed with ts3client_destroyServerConnectionHandler.

/* Disconnect from server */
ts3client_stopConnection(scHandlerID, "leaving");

We will not create an extra function for disconnecting, since it will only take one call. We'll be doing that directly in main() later.

Shutdown

When you're finished using the TeamSpeak SDK, shut it down:
unsigned int ts3client_destroyClientLib();.
Before calling this, be sure to release everything allocated by the TeamSpeak client library:

void ts_shutdown()
{
    ts3client_freeMemory(identity);  /* Release dynamically allocated memory */
    identity = NULL;

    /* Shutdown client lib */
    ts3client_destroyClientLib();
}

Audio

To initialize audio, open the playback and capture devices using the SDK functions ts3client_openCaptureDevice and ts3client_openPlaybackDevice. It is possible to address specific devices. Without passing a specific device as a parameter, the default devices defined in the operating system's audio settings are used.

Devices are opened on a per server connection handler basis. We create functions for opening and closing the audio devices, taking the connection ID as a parameter. First the function to start the audio devices:

void start_audio(uint64 connection_id)
{
    /* Open default playback device */
    ts3client_openPlaybackDevice(connection_id, "", NULL);

    /* Open default capture device */
    ts3client_openCaptureDevice(connection_id, "", NULL);
}

Now we create a function for stopping the audio:

void stop_audio(uint64 connection_id)
{
    // Close the default capture device
    ts3client_closeCaptureDevice(connection_id);

    // Close the default playback device
    ts3client_closePlaybackDevice(connection_id);
}

That's all we need for audio in the client, assuming we just want to use the default device of the operating system.

Putting it all together: main

At this point we've created a set of functions to handle specific tasks. Let's put the pieces together.
We create a int main(int argc, char **argv) function, and our very first call, you guessed it, will be ts_init(const char* soundbackends_path). We've added a helper function that gives you the proper path used in the sdk samples, so we include the common_client.h header and call ts_init(program_path(argv[0]));. Once the client is initialized, we want to connect, so we'll call const uint64 connection_id = ts_connect();. We check if the connection handler ID is valid and if so, start the audio devices via start_audio(connection_id);.

main will now look like that:

ts_init(program_path(argv[0]));

const uint64 connection_id = ts_connect();
if (connection_id != 0)
    start_audio(connection_id);

Before we continue, we want to wait until the client is connected (or has failed to do so). We've created a little helper function called bool wait_for_connection() in the full example to do just that. The function checks a connection_status variable regularly. We could ask the client lib about the status via unsigned int ts3client_getConnectionStatus(uint64 serverConnectionHandlerID, int* result);, however earlier we added a callback for connect status changes. Let's use that instead.

onConnectStatusChanged

We add a global variable int8_t connection_status and set it in the onConnectStatusChanged callback. While we're at it, we add logging for a connection timeout:

void onConnectStatusChangeEvent(uint64 connection_id, int new_status, unsigned int connection_error)
{
    if (new_status == STATUS_DISCONNECTED)
    {
        if (connection_error == ERROR_failed_connection_initialisation)
        {
            printf("Looks like there is no server running.\n");
        }
    }

    /*Update the global variable to the new state so the wait_for_connection
     function below can use it.*/
    connection_status = new_status;
}

Back to main

With the waiting for the connection added, we are letting the user know and prompt to disconnect on key input. Then we tell the TeamSpeak client library to disconnect and wait until this has happened:

if (connection_id != 0)
{
    start_audio(connection_id);

    if (wait_for_connection())
    {
        /* if we get here, we are fully connected */
        /* Wait for user input */
        printf("\n--- Press Return to disconnect from server and exit ---\n");
        getchar();

        /* Disconnect from server */
        ts3client_stopConnection(connection_id, "leaving");

        /* wait while the client is disconnecting */
        while (connection_status != STATUS_DISCONNECTED)
            sleep_ms(20);
    }

Since we started the audio devices, we now want to stop them: stop_audio(connection_id);. Now nothing depends on our server connection handler anymore, so we can destroy it: ts3client_destroyServerConnectionHandler(connection_id);.
Last, but not least - and not depending on the validity of the server connection handler - we call our shutdown() function to clean up.

The final main function:

int main(int argc, char **argv)
{
    ts_init(program_path(argv[0]));

    const uint64 connection_id = ts_connect();
    if (connection_id != 0)
    {
        start_audio(connection_id);

        if (wait_for_connection())
        {
            /* if we get here, we are fully connected */
            /* Wait for user input */
            printf("\n--- Press Return to disconnect from server and exit ---\n");
            getchar();

            /* Disconnect from server */
            ts3client_stopConnection(connection_id, "leaving");

            /* wait while the client is disconnecting */
            while (connection_status != STATUS_DISCONNECTED)
                sleep_ms(20);
        }

        stop_audio(connection_id);
        /* Destroy server connection handler */
        ts3client_destroyServerConnectionHandler(connection_id);
    }
    ts_shutdown();
    return 0;
}

Congratulations!

You have created your first application using the TeamSpeak SDK and have learned the basics to build upon.