首次推送
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
#include "connection_handler.hpp"
|
||||
|
||||
#include "ts_client.hpp"
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
/* We'll be using the create() function instead */
|
||||
Connection_Handler::Connection_Handler(uint64_t connection_id)
|
||||
: _connection_id(connection_id)
|
||||
{}
|
||||
|
||||
Connection_Handler::~Connection_Handler()
|
||||
{
|
||||
if (auto error = ts3client_destroyServerConnectionHandler(_connection_id); error != ERROR_ok)
|
||||
print_error(error, "Error destroying connection", _connection_id);
|
||||
}
|
||||
|
||||
/*static*/ std::unique_ptr<Connection_Handler> Connection_Handler::create()
|
||||
{
|
||||
auto connection_id = uint64_t{ 0 };
|
||||
if (auto error = ts3client_spawnNewServerConnectionHandler(0, &connection_id); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error spawning server connection handler", 0);
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<Connection_Handler>(connection_id);
|
||||
}
|
||||
|
||||
uint32_t Connection_Handler::connect()
|
||||
{
|
||||
/* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
|
||||
if (auto error = ts3client_startConnection(
|
||||
_connection_id,
|
||||
_connection_data.identity.c_str(),
|
||||
_connection_data.address.c_str(),
|
||||
_connection_data.port,
|
||||
_connection_data.nick.c_str(),
|
||||
nullptr,
|
||||
"",
|
||||
_connection_data.pw.c_str());
|
||||
error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error connecting to server", _connection_id);
|
||||
return error;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
uint32_t Connection_Handler::disconnect(std::string_view reason)
|
||||
{
|
||||
if (auto error = ts3client_stopConnection(_connection_id, reason.data()); error != ERROR_ok)
|
||||
{
|
||||
printf("Error stopping connection: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
void Connection_Handler::on_connect_status_change(ConnectStatus status, uint32_t error)
|
||||
{
|
||||
if (TS_Client::_do_autoreconnect && ConnectStatus::STATUS_DISCONNECTED == status)
|
||||
{
|
||||
// TODO: maybe should be timer-delayed?
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Connection_Handler::open_audio(Audio_IO audio_io, std::string_view mode, std::string_view device_id)
|
||||
{
|
||||
if (Audio_IO::Capture == audio_io)
|
||||
{
|
||||
if (auto error = ts3client_openPlaybackDevice(_connection_id, mode.data(), device_id.data()); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error opening playback device.", _connection_id);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else if (Audio_IO::Playback == audio_io)
|
||||
{
|
||||
if (auto error = ts3client_openCaptureDevice(_connection_id, mode.data(), device_id.data()); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error opening capture device.", _connection_id);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Turn off any DSP, except a very low power based Voice Activity Detection
|
||||
|
||||
/* Adjust "vad_mode" value to use power */
|
||||
print_error(
|
||||
ts3client_setPreProcessorConfigValue(_connection_id, "vad_mode", "1"),
|
||||
"Error setting vad_mode value to hybrid.", _connection_id);
|
||||
|
||||
/* Adjust "voiceactivation_level" value */
|
||||
print_error(
|
||||
ts3client_setPreProcessorConfigValue(_connection_id, "voiceactivation_level", "-50"),
|
||||
"Error setting voiceactivation_level.", _connection_id);
|
||||
|
||||
/* turn on vad */
|
||||
print_error(
|
||||
ts3client_setPreProcessorConfigValue(_connection_id, "vad", "true"),
|
||||
"Couldn't turn on VAD.", _connection_id);
|
||||
|
||||
// TODO: Turn off denoiser etc. no matter the default value
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <teamspeak/clientlib.h>
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
class Connection_Handler
|
||||
{
|
||||
public:
|
||||
/* We'll be using the create() function instead */
|
||||
Connection_Handler(uint64_t connection_id);
|
||||
Connection_Handler() = delete;
|
||||
~Connection_Handler();
|
||||
|
||||
static std::unique_ptr<Connection_Handler> create();
|
||||
|
||||
uint32_t connect();
|
||||
uint32_t disconnect(std::string_view reason = "leaving");
|
||||
|
||||
uint32_t open_audio(Audio_IO audio_io, std::string_view mode, std::string_view device_id);
|
||||
|
||||
void on_connect_status_change(ConnectStatus status, uint32_t error);
|
||||
|
||||
struct Connection_Data
|
||||
{
|
||||
std::string address = "";
|
||||
uint16_t port = 9987;
|
||||
std::string nick = "";
|
||||
std::string identity = "";
|
||||
std::string pw = "";
|
||||
};
|
||||
Connection_Data _connection_data;
|
||||
const uint64_t _connection_id;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "custom_device.hpp"
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <teamspeak/clientlib.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
Custom_Device::Custom_Device(uint32_t& error)
|
||||
{
|
||||
error = ts3client_registerCustomDevice(custom_device, custom_device, 48000, 1, 48000, 1);
|
||||
if (error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error creating custom device.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
Custom_Device::~Custom_Device()
|
||||
{
|
||||
/* Unregister the custom device. This automatically closes the device.*/
|
||||
if (auto error = ts3client_unregisterCustomDevice(custom_device); error != ERROR_ok)
|
||||
{
|
||||
printf("Error unregistering custom device: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
class Custom_Device
|
||||
{
|
||||
public:
|
||||
Custom_Device(uint32_t& error);
|
||||
~Custom_Device();
|
||||
|
||||
static constexpr const char* custom_mode = "custom";
|
||||
static constexpr const char* custom_device = "loopback";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <teamspeak/clientlib.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
namespace com::teamspeak {
|
||||
void print_error(uint32_t error, const std::string& msg, uint64_t connection_id)
|
||||
{
|
||||
if (error == ERROR_ok)
|
||||
return;
|
||||
|
||||
char* errormsg = nullptr;
|
||||
if (ts3client_getErrorMessage(error, &errormsg) == ERROR_ok)
|
||||
{
|
||||
auto error_msg = msg + " " + std::string(errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
ts3client_logMessage(error_msg.c_str(), LogLevel_ERROR, "", connection_id);
|
||||
return;
|
||||
}
|
||||
ts3client_logMessage(msg.c_str(), LogLevel_ERROR, "", connection_id);
|
||||
}
|
||||
|
||||
auto create_identity() -> std::string
|
||||
{
|
||||
/* Create a new client identity */
|
||||
/* In your real application you should do this only once, store the assigned identity locally and then reuse it. */
|
||||
auto result = std::string();
|
||||
char* identity = nullptr;
|
||||
if (auto error = ts3client_createIdentity(&identity); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error creating identity", 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = std::string(identity);
|
||||
ts3client_freeMemory(identity); /* Release dynamically allocated memory */
|
||||
identity = nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace com::teamspeak {
|
||||
|
||||
enum Audio_IO : uint8_t
|
||||
{
|
||||
Playback = 0,
|
||||
Capture
|
||||
};
|
||||
|
||||
void print_error(uint32_t error, const std::string& msg, uint64_t connection_id = 0);
|
||||
auto create_identity()->std::string;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* TeamSpeak SDK client repeater sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include "custom_device.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "ts_client.hpp"
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/clientlib.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#define strdup(x) _strdup(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
struct Opts {
|
||||
std::string from_ip = "";
|
||||
uint16_t from_port = 0;
|
||||
std::string to_ip = "";
|
||||
uint16_t to_port = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
char* programPath(char* programInvocation)
|
||||
{
|
||||
char* path;
|
||||
char* end;
|
||||
int length;
|
||||
char pathsep;
|
||||
|
||||
if (programInvocation == NULL) return strdup("");
|
||||
|
||||
#ifdef _WIN32
|
||||
pathsep = '\\';
|
||||
#else
|
||||
pathsep = '/';
|
||||
#endif
|
||||
|
||||
end = strrchr(programInvocation, pathsep);
|
||||
if (!end) return strdup("");
|
||||
|
||||
length = (end - programInvocation) + 2;
|
||||
path = (char*)malloc(length);
|
||||
strncpy(path, programInvocation, length - 1);
|
||||
path[length - 1] = 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
std::cout << "usage: from_id from_port to_id to_port" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// TODO: Decide on a proper header only options parser
|
||||
auto opts = Opts();
|
||||
if (argc != 5)
|
||||
{
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
for (auto i = decltype(argc){1}; i < argc; ++i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
opts.from_ip = std::string(argv[i]);
|
||||
break;
|
||||
case 2:
|
||||
try
|
||||
{
|
||||
opts.from_port = std::stoul(argv[i]);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
opts.to_ip = std::string(argv[i]);
|
||||
break;
|
||||
case 4:
|
||||
try
|
||||
{
|
||||
opts.to_port = std::stoul(argv[i]);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << "listening to " << opts.from_ip.c_str() << ":" << opts.from_port << ", sending to " << opts.to_ip.c_str() << ":" << opts.to_port << std::endl;
|
||||
|
||||
using namespace com::teamspeak;
|
||||
|
||||
{
|
||||
auto* path = programPath(argv[0]);
|
||||
auto success = TS_Client::create(path);
|
||||
free(path);
|
||||
if (!success)
|
||||
return 1;
|
||||
}
|
||||
auto&& ts_client = TS_Client::ts_client;
|
||||
{
|
||||
auto identity = create_identity();
|
||||
if (identity.empty())
|
||||
return 1;
|
||||
|
||||
ts_client->_identity = identity;
|
||||
}
|
||||
|
||||
// We'll recycle them in case of disconnect, hence spawn these only once
|
||||
{
|
||||
auto connection = Connection_Handler::create();
|
||||
if (!connection)
|
||||
return 1;
|
||||
|
||||
ts_client->_connections[Audio_IO::Playback].swap(connection);
|
||||
}
|
||||
|
||||
{
|
||||
auto connection = Connection_Handler::create();
|
||||
if (!connection)
|
||||
return 1;
|
||||
|
||||
ts_client->_connections[Audio_IO::Capture].swap(connection);
|
||||
}
|
||||
|
||||
|
||||
auto&& connection_listen = ts_client->_connections[Audio_IO::Playback];
|
||||
auto&& connection_broadcast = ts_client->_connections[Audio_IO::Capture];
|
||||
|
||||
connection_listen->open_audio(Audio_IO::Playback, Custom_Device::custom_mode, Custom_Device::custom_device);
|
||||
connection_broadcast->open_audio(Audio_IO::Capture, Custom_Device::custom_mode, Custom_Device::custom_device);
|
||||
|
||||
connection_listen->_connection_data = Connection_Handler::Connection_Data{
|
||||
opts.from_ip,
|
||||
opts.from_port,
|
||||
"repeater-listener",
|
||||
ts_client->_identity
|
||||
};
|
||||
connection_listen->connect();
|
||||
connection_broadcast->_connection_data = Connection_Handler::Connection_Data{
|
||||
opts.to_ip,
|
||||
opts.to_port,
|
||||
"repeater-broadcaster",
|
||||
ts_client->_identity
|
||||
};
|
||||
connection_broadcast->connect();
|
||||
|
||||
// 48000 kHz, 1ch, 20ms -> 960 samples
|
||||
auto playback_buffer = std::array<int16_t, 960>();
|
||||
auto custom_audio_thread = std::thread([&playback_buffer]()
|
||||
{
|
||||
while (!TS_Client::ts_client->_shutting_down)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
/* Get playback data from the client lib */
|
||||
if (auto error_playback = ts3client_acquireCustomPlaybackData(Custom_Device::custom_device, playback_buffer.data(), playback_buffer.size()); error_playback != ERROR_ok)
|
||||
{
|
||||
if (ERROR_sound_no_data == error_playback)
|
||||
{
|
||||
/* Not an error. The client lib has no playback data available.
|
||||
Depending on your custom sound API, either pause playback for
|
||||
performance optimization or send a buffer of zeros. */
|
||||
|
||||
// we're doing a no-op here
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error occured */
|
||||
print_error(error_playback, "Failed to get playback data", 0);
|
||||
}
|
||||
break; // break draining (inner loop)
|
||||
}
|
||||
else
|
||||
{
|
||||
// we got playback data, loop it back to capture
|
||||
/* Stream your capture data to the client lib */
|
||||
if (auto error_capture = ts3client_processCustomCaptureData(Custom_Device::custom_device, playback_buffer.data(), playback_buffer.size()); ERROR_ok != error_capture)
|
||||
{
|
||||
print_error(error_capture, "Failed to process capture data", 0);
|
||||
break; // break draining (inner loop)
|
||||
}
|
||||
}
|
||||
}
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(0.02s); // audio buffer size in our opus is 20ms
|
||||
}
|
||||
});
|
||||
|
||||
SLEEP(500);
|
||||
|
||||
/* Wait for user input */
|
||||
printf("\n--- Press Return to disconnect from server and exit ---\n");
|
||||
getchar();
|
||||
|
||||
/* Disconnect from servers */
|
||||
ts_client->_shutting_down = true;
|
||||
custom_audio_thread.join();
|
||||
connection_listen->disconnect();
|
||||
connection_broadcast->disconnect();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ts_client.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ts_client.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/connection_handler.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/connection_handler.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/helpers.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/helpers.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/custom_device.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/custom_device.cpp"
|
||||
)
|
||||
@@ -0,0 +1,221 @@
|
||||
#include "ts_client.hpp"
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <teamspeak/clientlib.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
/*static*/ std::unique_ptr<TS_Client> TS_Client::ts_client;
|
||||
|
||||
TS_Client::TS_Client(std::string_view path, bool& success)
|
||||
{
|
||||
success = true;
|
||||
/* 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. */
|
||||
|
||||
/*
|
||||
* Callback for connection status change.
|
||||
* Connection status switches through the states STATUS_DISCONNECTED, STATUS_CONNECTING, STATUS_CONNECTED and STATUS_CONNECTION_ESTABLISHED.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* newStatus - New connection status, see the enum ConnectStatus in public_definitions.h
|
||||
* errorNumber - Error code. Should be zero when connecting or actively disconnection.
|
||||
* Contains error state when losing connection.
|
||||
*/
|
||||
funcs.onConnectStatusChangeEvent = [](uint64_t connection_id, int32_t status, uint32_t error)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_connect_status_change(connection_id, static_cast<ConnectStatus>(status), error);
|
||||
};
|
||||
funcs.onClientMoveEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* /*msg*/)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
funcs.onClientMoveSubscriptionEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
funcs.onClientMoveTimeoutEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* /*msg*/)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
funcs.onClientMoveMovedEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID /*moverID*/, const char* /*moverName*/, const char* /*moverUniqueIdentifier*/, const char* /*msg*/)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
funcs.onClientKickFromChannelEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID /*kickerID*/, const char* /*kickerName*/, const char* /*kickerUniqueIdentifier*/, const char* /*msg*/)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
funcs.onClientKickFromServerEvent = [](uint64 connection_id, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID /*kickerID */, const char* /*kickerName*/, const char* /*kickerUniqueIdentifier*/, const char* /*msg*/)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
TS_Client::ts_client->on_client_move_common(connection_id, clientID, oldChannelID, newChannelID, static_cast<Visibility>(visibility));
|
||||
};
|
||||
/*
|
||||
* This event is called when a client starts or stops talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* status - 1 if client starts talking, 0 if client stops talking
|
||||
* isReceivedWhisper - 1 if this event was caused by whispering, 0 if caused by normal talking
|
||||
* clientID - ID of the client who announced the talk status change
|
||||
*/
|
||||
funcs.onTalkStatusChangeEvent = [](uint64 serverConnectionHandlerID, int status, int isReceivedWhisper, anyID clientID)
|
||||
{
|
||||
char* name = nullptr;
|
||||
/* Query client nickname from ID */
|
||||
if (ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
|
||||
return;
|
||||
|
||||
auto status_str = std::string();
|
||||
switch (status)
|
||||
{
|
||||
case TalkStatus::STATUS_TALKING:
|
||||
status_str = "starts";
|
||||
break;
|
||||
case TalkStatus::STATUS_NOT_TALKING:
|
||||
status_str = "stops";
|
||||
break;
|
||||
case TalkStatus::STATUS_TALKING_WHILE_DISABLED:
|
||||
status_str = "starts (while disabled)";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::cout << "Client " << name << " " << status_str << " talking." << std::endl;
|
||||
/* Release dynamically allocated memory only if function succeeded */
|
||||
ts3client_freeMemory(name);
|
||||
};
|
||||
funcs.onServerErrorEvent = [](uint64 connection_id, const char* error_msg, uint32_t error, const char* /*return_code*/, const char* extra_msg)
|
||||
{
|
||||
auto msg = std::string("onServerError: ");
|
||||
if (error_msg)
|
||||
msg += error_msg;
|
||||
|
||||
if (extra_msg)
|
||||
{
|
||||
auto extra = std::string(extra_msg);
|
||||
if (!extra.empty())
|
||||
msg += " Extra Msg: " + extra;
|
||||
}
|
||||
if (error == ERROR_ok)
|
||||
ts3client_logMessage(msg.c_str(), LogLevel::LogLevel_DEBUG, "", connection_id);
|
||||
else
|
||||
print_error(error, msg, connection_id);
|
||||
};
|
||||
funcs.onIgnoredWhisperEvent = [](uint64 connection_id, anyID client_id)
|
||||
{
|
||||
print_error(ts3client_allowWhispersFrom(connection_id, client_id), "Error allowing whisper", connection_id);
|
||||
};
|
||||
|
||||
if (auto error = ts3client_initClientLib(&funcs, nullptr, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, nullptr, path.data()); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Error initialzing clientlib", 0);
|
||||
success = false;
|
||||
}
|
||||
_funcs = funcs;
|
||||
|
||||
if (success)
|
||||
{
|
||||
auto error = uint32_t{ ERROR_ok };
|
||||
_custom_device = std::make_unique<Custom_Device>(error);
|
||||
if (ERROR_ok != error)
|
||||
_custom_device = {};
|
||||
|
||||
success = ERROR_ok == error;
|
||||
}
|
||||
}
|
||||
|
||||
TS_Client::~TS_Client()
|
||||
{
|
||||
if (auto error = ts3client_destroyClientLib(); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Failed to destroy clientlib", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool TS_Client::create(std::string_view path)
|
||||
{
|
||||
if (TS_Client::ts_client)
|
||||
return false;
|
||||
|
||||
auto success = false;
|
||||
auto result = std::make_unique<TS_Client>(path, success);
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
success = result->log_clientlib_version();
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
TS_Client::ts_client.swap(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TS_Client::log_clientlib_version()
|
||||
{
|
||||
char* version = nullptr;
|
||||
if (auto error = ts3client_getClientLibVersion(&version); error != ERROR_ok)
|
||||
{
|
||||
print_error(error, "Failed to get clientlib version", 0);
|
||||
return false;
|
||||
}
|
||||
auto msg = "Client lib version: " + std::string(version);
|
||||
ts3client_freeMemory(version); /* Release dynamically allocated memory */
|
||||
version = nullptr;
|
||||
ts3client_logMessage(msg.c_str(), LogLevel_INFO, "", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TS_Client::on_client_move_common(uint64_t connection_id, uint16_t client_id, uint64_t oldChannelID, uint64_t newChannelID, Visibility visibility)
|
||||
{
|
||||
}
|
||||
|
||||
void TS_Client::on_connect_status_change(uint64_t connection_id, ConnectStatus status, uint32_t error)
|
||||
{
|
||||
{
|
||||
auto msg = std::string("Connect status changed: ") + std::to_string(connection_id) + " " + std::to_string(status);
|
||||
ts3client_logMessage(msg.c_str(), LogLevel_INFO, "", connection_id);
|
||||
}
|
||||
/* Failed to connect ? */
|
||||
if (status == STATUS_DISCONNECTED && error == ERROR_failed_connection_initialisation)
|
||||
{
|
||||
ts3client_logMessage("Looks like there is no server running.\n", LogLevel_INFO, "", connection_id);
|
||||
}
|
||||
print_error(error, "onConnectStatusChange", connection_id);
|
||||
|
||||
if (_shutting_down)
|
||||
return;
|
||||
|
||||
/* pass the event on to the connection instance */
|
||||
if (auto it = std::find_if(std::begin(_connections), std::end(_connections), [connection_id](auto&& connection)
|
||||
{
|
||||
return connection && connection_id == connection->_connection_id;
|
||||
}); it != std::end(_connections))
|
||||
{
|
||||
auto&& connection = *it;
|
||||
connection->on_connect_status_change(status, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "connection_handler.hpp"
|
||||
#include "custom_device.hpp"
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace com::teamspeak
|
||||
{
|
||||
class TS_Client
|
||||
{
|
||||
public:
|
||||
TS_Client(std::string_view path, bool& success);
|
||||
~TS_Client();
|
||||
|
||||
static bool create(std::string_view path);
|
||||
|
||||
bool log_clientlib_version();
|
||||
|
||||
void on_client_move_common(uint64_t connection_id, uint16_t client_id, uint64_t old_channel_id, uint64_t new_channel_id, Visibility visibility);
|
||||
void on_connect_status_change(uint64_t connection_id, ConnectStatus status, uint32_t error);
|
||||
|
||||
ClientUIFunctions _funcs;
|
||||
std::string _identity = "";
|
||||
std::array<std::unique_ptr<Connection_Handler>, 2> _connections;
|
||||
bool _shutting_down = false;
|
||||
static constexpr bool _do_autoreconnect{ true };
|
||||
private:
|
||||
std::unique_ptr<Custom_Device> _custom_device;
|
||||
public:
|
||||
static std::unique_ptr<TS_Client> ts_client;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user