首次推送
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
project("TeamSpeak SDK Samples" LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_MACOSX_RPATH TRUE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
if (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH "@executable_path")
|
||||
else()
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
|
||||
endif()
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
|
||||
set(BUILD_RPATH_USE_ORIGIN TRUE)
|
||||
|
||||
find_package(team_client CONFIG)
|
||||
find_package(team_server CONFIG)
|
||||
|
||||
if(NOT team_client_FOUND AND NOT team_server_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"Neither team_client nor team_server SDK found via CMAKE_PREFIX_PATH. "
|
||||
"Pass -DCMAKE_PREFIX_PATH=... pointing at a stage directory.")
|
||||
endif()
|
||||
|
||||
set(TS_SAMPLES
|
||||
client
|
||||
client_customdevice
|
||||
client_minimal
|
||||
client_minimal_filetransfer
|
||||
client_multi
|
||||
client_cpp_repeater
|
||||
server
|
||||
server_creation_params
|
||||
server_filetransfer
|
||||
server_minimal
|
||||
server_permissions
|
||||
)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# A sample's SDK flavor is given by its folder-name prefix: client* links the client SDK, server*
|
||||
# the server SDK.
|
||||
foreach(sample_folder IN LISTS TS_SAMPLES)
|
||||
if(sample_folder MATCHES "^client")
|
||||
set(sample_type client)
|
||||
elseif(sample_folder MATCHES "^server")
|
||||
set(sample_type server)
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot infer SDK type from sample folder '${sample_folder}' (expected client*/server*)")
|
||||
endif()
|
||||
if("${sample_type}" STREQUAL "client" AND NOT team_client_FOUND)
|
||||
continue()
|
||||
endif()
|
||||
if("${sample_type}" STREQUAL "server" AND NOT team_server_FOUND)
|
||||
continue()
|
||||
endif()
|
||||
if("${sample_type}" STREQUAL "server"
|
||||
AND CMAKE_SYSTEM_NAME STREQUAL "Linux"
|
||||
AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(ts_sample_bin "ts_${sample_folder}")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/${sample_folder}/sources.cmake")
|
||||
|
||||
add_executable(${ts_sample_bin} ${TS_SAMPLE_SRC})
|
||||
set_target_properties(${ts_sample_bin} PROPERTIES CXX_STANDARD 17)
|
||||
source_group("" FILES ${TS_SAMPLE_SRC})
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmake/ide.cmake")
|
||||
if(TS_SDK_IDE_FILES)
|
||||
target_sources(${ts_sample_bin} PRIVATE ${TS_SDK_IDE_FILES})
|
||||
source_group("teamspeak" FILES ${TS_SDK_IDE_FILES})
|
||||
endif()
|
||||
|
||||
if("${sample_type}" STREQUAL "client")
|
||||
target_link_libraries(${ts_sample_bin} PRIVATE teamspeak::client)
|
||||
if(APPLE)
|
||||
set_target_properties(${ts_sample_bin} PROPERTIES
|
||||
INSTALL_RPATH "@executable_path"
|
||||
)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${ts_sample_bin} PRIVATE Threads::Threads)
|
||||
set(ts_sdk_target teamspeak::client)
|
||||
elseif("${sample_type}" STREQUAL "server")
|
||||
target_link_libraries(${ts_sample_bin} PRIVATE teamspeak::server)
|
||||
if(APPLE)
|
||||
set_target_properties(${ts_sample_bin} PROPERTIES
|
||||
INSTALL_RPATH "@executable_path"
|
||||
)
|
||||
endif()
|
||||
set(ts_sdk_target teamspeak::server)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(${ts_sample_bin} PRIVATE dl)
|
||||
endif()
|
||||
|
||||
# Copy the SDK shared library next to the sample so it runs from the build tree.
|
||||
add_custom_command(TARGET ${ts_sample_bin} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_FILE:${ts_sdk_target}>"
|
||||
"$<TARGET_FILE_DIR:${ts_sample_bin}>"
|
||||
)
|
||||
|
||||
install(TARGETS ${ts_sample_bin} RUNTIME DESTINATION bin)
|
||||
endforeach()
|
||||
|
||||
set(_ts_imported_libs "")
|
||||
if(team_client_FOUND)
|
||||
list(APPEND _ts_imported_libs teamspeak::client)
|
||||
endif()
|
||||
if(team_server_FOUND)
|
||||
list(APPEND _ts_imported_libs teamspeak::server)
|
||||
endif()
|
||||
if(_ts_imported_libs)
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS ${_ts_imported_libs}
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION bin
|
||||
)
|
||||
endif()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* TeamSpeak SDK client minimal sample with custom
|
||||
* capture and record
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
/* This example connects to a server and plays a wave file, while
|
||||
recording incomming sound to output.wav */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#pragma warning(disable : 4996)
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/clientlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
#include "wave.h"
|
||||
|
||||
/*The client lib works at 48Khz internally.
|
||||
It is therefore advisable to use the same for your project */
|
||||
#define PLAYBACK_FREQUENCY 48000
|
||||
#define PLAYBACK_CHANNELS 2
|
||||
|
||||
#define AUDIO_PROCESS_SECONDS 500
|
||||
|
||||
/*
|
||||
* 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 clientlib_publicdefinitions.h
|
||||
* errorNumber - Error code. Should be zero when connecting or actively disconnection.
|
||||
* Contains error state when losing connection.
|
||||
*/
|
||||
void onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
|
||||
printf("Connect status changed: %llu %d %u\n", (unsigned long long)serverConnectionHandlerID, newStatus, errorNumber);
|
||||
/* Failed to connect ? */
|
||||
if(newStatus == STATUS_DISCONNECTED && errorNumber == ERROR_failed_connection_initialisation) {
|
||||
printf("Looks like there is no server running!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for current channels being announced to the client after connecting to a server.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the announced channel
|
||||
* channelParentID - ID of the parent channel
|
||||
*/
|
||||
void onNewChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID) {
|
||||
/* Query channel name from channel ID */
|
||||
char* name;
|
||||
unsigned int error;
|
||||
|
||||
printf("onNewChannelEvent: %llu %llu %llu\n", (unsigned long long)serverConnectionHandlerID, (unsigned long long)channelID, (unsigned long long)channelParentID);
|
||||
if((error = ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name)) == ERROR_ok) {
|
||||
printf("New channel: %llu %s \n", (unsigned long long)channelID, name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
} else {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error getting channel name in onNewChannelEvent: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for just created channels.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the announced channel
|
||||
* channelParentID - ID of the parent channel
|
||||
* invokerID - ID of the client who created the channel
|
||||
* invokerName - Name of the client who created the channel
|
||||
*/
|
||||
void onNewChannelCreatedEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
|
||||
char* name;
|
||||
|
||||
/* Query channel name from channel ID */
|
||||
if(ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name) != ERROR_ok)
|
||||
return;
|
||||
printf("New channel created: %s\n", name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when a channel was deleted.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the deleted channel
|
||||
* invokerID - ID of the client who deleted the channel
|
||||
* invokerName - Name of the client who deleted the channel
|
||||
*/
|
||||
void onDelChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
|
||||
printf("Channel ID %llu deleted by %s (%u)\n", (unsigned long long)channelID, invokerName, invokerID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a client joins, leaves or moves to another channel.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the moved client
|
||||
* oldChannelID - ID of the old channel left by the client
|
||||
* newChannelID - ID of the new channel joined by the client
|
||||
* visibility - Visibility of the moved client. See the enum Visibility in clientlib_publicdefinitions.h
|
||||
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
|
||||
*/
|
||||
void onClientMoveEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* moveMessage) {
|
||||
printf("ClientID %u moves from channel %llu to %llu with message %s\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, moveMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for other clients in current and subscribed channels being announced to the client.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the announced client
|
||||
* oldChannelID - ID of the subscribed channel where the client left visibility
|
||||
* newChannelID - ID of the subscribed channel where the client entered visibility
|
||||
* visibility - Visibility of the announced client. See the enum Visibility in clientlib_publicdefinitions.h
|
||||
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
|
||||
*/
|
||||
void onClientMoveSubscriptionEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility) {
|
||||
char* name;
|
||||
|
||||
/* Query client nickname from ID */
|
||||
if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
|
||||
return;
|
||||
printf("New client: %s\n", name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a client drops his connection.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the moved client
|
||||
* oldChannelID - ID of the channel the leaving client was previously member of
|
||||
* newChannelID - 0, as client is leaving
|
||||
* visibility - Always LEAVE_VISIBILITY
|
||||
* timeoutMessage - Optional message giving the reason for the timeout
|
||||
*/
|
||||
void onClientMoveTimeoutEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* timeoutMessage) {
|
||||
printf("ClientID %u timeouts with message %s\n",clientID, timeoutMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
void onTalkStatusChangeEvent(uint64 serverConnectionHandlerID, int status, int isReceivedWhisper, anyID clientID) {
|
||||
char* name;
|
||||
|
||||
/* Query client nickname from ID */
|
||||
if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
|
||||
return;
|
||||
if(status == STATUS_TALKING) {
|
||||
printf("Client \"%s\" starts talking.\n", name);
|
||||
} else {
|
||||
printf("Client \"%s\" stops talking.\n", name);
|
||||
}
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
|
||||
printf("Error for server %llu: %s %s\n", (unsigned long long)serverConnectionHandlerID, errorMessage, extraMessage);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
uint64 scHandlerID;
|
||||
unsigned int error;
|
||||
char *version;
|
||||
char *identity;
|
||||
|
||||
int captureFrequency;
|
||||
int captureChannels;
|
||||
short* captureBuffer;
|
||||
int captureBufferSamples;
|
||||
|
||||
int audioPeriodCounter;
|
||||
int captureAudioOffset;
|
||||
int capturePeriodSize;
|
||||
|
||||
short* playbackBuffer;
|
||||
int playbackAudioOffset;
|
||||
int playbackPeriodSize;
|
||||
|
||||
char* path;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ClientUIFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;
|
||||
funcs.onNewChannelEvent = onNewChannelEvent;
|
||||
funcs.onNewChannelCreatedEvent = onNewChannelCreatedEvent;
|
||||
funcs.onDelChannelEvent = onDelChannelEvent;
|
||||
funcs.onClientMoveEvent = onClientMoveEvent;
|
||||
funcs.onClientMoveSubscriptionEvent = onClientMoveSubscriptionEvent;
|
||||
funcs.onClientMoveTimeoutEvent = onClientMoveTimeoutEvent;
|
||||
funcs.onTalkStatusChangeEvent = onTalkStatusChangeEvent;
|
||||
funcs.onServerErrorEvent = onServerErrorEvent;
|
||||
|
||||
/* Read in the wave we are going to stream to the server */
|
||||
if (!readWave("welcome_to_teamspeak.wav", &captureFrequency, &captureChannels, &captureBuffer, &captureBufferSamples))
|
||||
return 1;
|
||||
|
||||
/* allocate AUDIO_PROCESS_SECONDS seconds worth of PLAYBACK_FREQUENCY 16bit PLAYBACK_CHANNELS channels */
|
||||
playbackBuffer = (short*) malloc(AUDIO_PROCESS_SECONDS * PLAYBACK_FREQUENCY * sizeof(short) * PLAYBACK_CHANNELS);
|
||||
if (!playbackBuffer){
|
||||
printf("error: could not allocate memory for output wave\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initialize client lib with callbacks */
|
||||
path = programPath(argv[0]);
|
||||
if((error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE, NULL, path)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* register a new custom sound device, that captures at read wave freq+channels and plays PLAYBACK_CHANNELS channels at PLAYBACK_FREQUENCY */
|
||||
if ((error = ts3client_registerCustomDevice("customWaveDeviceId", "Nice displayable wave device name", captureFrequency, captureChannels, PLAYBACK_FREQUENCY, PLAYBACK_CHANNELS)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error registering custom sound device: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Spawn a new server connection handler using the default port and store the server ID */
|
||||
if((error = ts3client_spawnNewServerConnectionHandler(0, &scHandlerID)) != ERROR_ok) {
|
||||
printf("Error spawning server connection handler: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open capture device we created earlier */
|
||||
if((error = ts3client_openCaptureDevice(scHandlerID, "custom", "customWaveDeviceId")) != ERROR_ok) {
|
||||
printf("Error opening capture device: %d\n", error);
|
||||
}
|
||||
|
||||
/* Open playback device we created earlier */
|
||||
if((error = ts3client_openPlaybackDevice(scHandlerID, "custom", "customWaveDeviceId")) != ERROR_ok) {
|
||||
printf("Error opening playback device: %d\n", error);
|
||||
}
|
||||
|
||||
/* Create a new client identity */
|
||||
/* In your real application you should do this only once, store the assigned identity locally and then reuse it. */
|
||||
if((error = ts3client_createIdentity(&identity)) != ERROR_ok) {
|
||||
printf("Error creating identity: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
|
||||
if((error = ts3client_startConnection(scHandlerID, identity, "localhost", 9987, "client", NULL, "", "secret")) != ERROR_ok) {
|
||||
printf("Error connecting to server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ts3client_freeMemory(identity); /* Release dynamically allocated memory */
|
||||
identity = NULL;
|
||||
|
||||
printf("Client lib initialized and running\n");
|
||||
|
||||
/* Query and print client lib version */
|
||||
if((error = ts3client_getClientLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Failed to get clientlib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Client lib version: %s\n", version);
|
||||
ts3client_freeMemory(version); /* Release dynamically allocated memory */
|
||||
version = NULL;
|
||||
|
||||
SLEEP(500);
|
||||
|
||||
printf("\n--- processing audio for %d seconds ---\n", AUDIO_PROCESS_SECONDS);
|
||||
|
||||
/*the clientlib works with 20ms packets internaly.
|
||||
So the best is to feed is 20ms worth of sound at a time */
|
||||
capturePeriodSize = (captureFrequency*20)/1000;
|
||||
playbackPeriodSize = (PLAYBACK_FREQUENCY*20)/1000;
|
||||
|
||||
captureAudioOffset = 0;
|
||||
playbackAudioOffset = 0;
|
||||
for(audioPeriodCounter = 0; audioPeriodCounter < 50*AUDIO_PROCESS_SECONDS; ++audioPeriodCounter){ /*50*20=1000*/
|
||||
/* wait 20 ms */
|
||||
SLEEP(20);
|
||||
|
||||
/* make sure we dont stream past the end of our wave sample */
|
||||
if (captureAudioOffset + capturePeriodSize > captureBufferSamples)
|
||||
captureAudioOffset = 0;
|
||||
|
||||
/* stream capture data to the client lib */
|
||||
if((error = ts3client_processCustomCaptureData("customWaveDeviceId", captureBuffer + captureAudioOffset*captureChannels, capturePeriodSize)) != ERROR_ok){
|
||||
printf("Failed to get stream capture data: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get playback data from the client lib */
|
||||
if((error = ts3client_acquireCustomPlaybackData("customWaveDeviceId", playbackBuffer + playbackAudioOffset*PLAYBACK_CHANNELS, playbackPeriodSize))!= ERROR_ok){
|
||||
if(error != ERROR_sound_no_data) { //this error signals us to play silence
|
||||
printf("Failed to get acquire playback data: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
memset(playbackBuffer + playbackAudioOffset * PLAYBACK_CHANNELS, 0, playbackPeriodSize * PLAYBACK_CHANNELS * sizeof(short));
|
||||
}
|
||||
|
||||
/*update buffer offsets */
|
||||
captureAudioOffset += capturePeriodSize;
|
||||
playbackAudioOffset += playbackPeriodSize;
|
||||
}
|
||||
|
||||
/* Disconnect from server. ERROR_not_connected just means the peer is already
|
||||
gone (e.g. the server was shut down) - nothing to do, so move on. */
|
||||
if((error = ts3client_stopConnection(scHandlerID, "leaving")) != ERROR_ok && error != ERROR_not_connected)
|
||||
printf("Error stopping connection: %d\n", error);
|
||||
|
||||
/* Destroy server connection handler */
|
||||
if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok)
|
||||
printf("Error destroying ServerConnectionHandler: %d\n", error);
|
||||
|
||||
/* unregister the custom sound device */
|
||||
if ((error = ts3client_unregisterCustomDevice("customWaveDeviceId")) != ERROR_ok)
|
||||
printf("Error unregisterring custom sound device: %d\n", error);
|
||||
|
||||
/* Shutdown client lib */
|
||||
if((error = ts3client_destroyClientLib()) != ERROR_ok)
|
||||
printf("Failed to destroy clientlib: %d\n", error);
|
||||
|
||||
/* save the playback data */
|
||||
writeWave("output.wav", PLAYBACK_FREQUENCY, PLAYBACK_CHANNELS, playbackBuffer, PLAYBACK_FREQUENCY*AUDIO_PROCESS_SECONDS);
|
||||
|
||||
/* release allocated memory */
|
||||
free(captureBuffer);
|
||||
free(playbackBuffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/wave.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/wave.h"
|
||||
)
|
||||
@@ -0,0 +1,111 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include "wave.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
char riff[4] = { 'R', 'I', 'F', 'F' };
|
||||
char wave[4] = { 'W', 'A', 'V', 'E' };
|
||||
char fmt[4] = { 'f', 'm', 't', ' ' };
|
||||
char dat[4] = { 'd', 'a', 't', 'a' };
|
||||
|
||||
void writeWave(const char* filename, int freq, int channels, short* buffer, int samples) {
|
||||
struct WaveHeader wh;
|
||||
int i;
|
||||
int elemWritten;
|
||||
FILE *f;
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
wh.riffId[i] = riff[i];
|
||||
wh.riffType[i] = wave[i];
|
||||
wh.fmtId[i] = fmt[i];
|
||||
wh.dataId[i] = dat[i];
|
||||
}
|
||||
|
||||
/* Format chunk */
|
||||
wh.fmtLen = 16;
|
||||
wh.formatTag = 1; /* PCM */
|
||||
wh.channels = channels;
|
||||
wh.samplesPerSec = freq;
|
||||
wh.avgBytesPerSec = freq * channels * sizeof(short);
|
||||
wh.blockAlign = channels * sizeof(short);
|
||||
wh.bitsPerSample = sizeof(short)*8;
|
||||
wh.dataLen = samples * channels * sizeof(short);
|
||||
wh.len = 36 + wh.dataLen;
|
||||
|
||||
f = fopen(filename,"wb");
|
||||
if (!f) {
|
||||
printf("error: could not write wave\n");
|
||||
return;
|
||||
}
|
||||
|
||||
elemWritten = fwrite(&wh, sizeof(wh), 1, f);
|
||||
if (elemWritten) elemWritten = fwrite(buffer, wh.dataLen, 1, f);
|
||||
fclose(f);
|
||||
|
||||
if (!elemWritten){
|
||||
printf("error: could not write wave\n");
|
||||
}
|
||||
}
|
||||
|
||||
int readWave(const char* filename, int* freq, int* channels, short** buffer, int* samples) {
|
||||
struct WaveHeader wh;
|
||||
FILE *f;
|
||||
int i;
|
||||
int elemsRead;
|
||||
|
||||
memset(&wh, 0, sizeof(wh));
|
||||
|
||||
f = fopen(filename,"rb");
|
||||
if (!f) {
|
||||
printf("error: could not open wave %s\n",filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(&wh, sizeof(wh), 1, f);
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
if ((wh.riffId[i] != riff[i]) ||
|
||||
(wh.riffType[i] != wave[i]) ||
|
||||
(wh.fmtId[i] != fmt[i]) ||
|
||||
(wh.dataId[i] != dat[i])){
|
||||
goto closeError;
|
||||
}
|
||||
}
|
||||
// Format chunk
|
||||
if (wh.fmtLen != 16) goto closeError;
|
||||
if (wh.formatTag != 1) goto closeError;
|
||||
*channels = wh.channels;
|
||||
if (*channels <1 || *channels >2) goto closeError;
|
||||
*freq = wh.samplesPerSec;
|
||||
if (wh.blockAlign != *channels * sizeof(short)) goto closeError;
|
||||
*samples = wh.dataLen / (*channels * sizeof(short));
|
||||
|
||||
if (*samples < *freq) {
|
||||
fclose(f);
|
||||
printf("error: wave file is too short\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*buffer) = (short*) malloc(wh.dataLen);
|
||||
if (!*buffer){
|
||||
printf("error: could not allocate memory for wave\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
elemsRead = fread(*buffer, wh.dataLen, 1, f);
|
||||
fclose(f);
|
||||
if (elemsRead != 1){
|
||||
printf("error: reading wave file\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
closeError:
|
||||
fclose(f);
|
||||
printf("error: invalid wave file %s\n",filename);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef WAVE_H
|
||||
#define WAVE_H
|
||||
|
||||
struct WaveHeader {
|
||||
// Riff chunk
|
||||
char riffId[4]; // 'RIFF'
|
||||
unsigned int len;
|
||||
char riffType[4]; // 'WAVE'
|
||||
|
||||
// Format chunk
|
||||
char fmtId[4]; // 'fmt '
|
||||
unsigned int fmtLen;
|
||||
unsigned short formatTag;
|
||||
unsigned short channels;
|
||||
unsigned int samplesPerSec;
|
||||
unsigned int avgBytesPerSec;
|
||||
unsigned short blockAlign;
|
||||
unsigned short bitsPerSample;
|
||||
|
||||
// Data chunk
|
||||
char dataId[4]; // 'data'
|
||||
unsigned int dataLen;
|
||||
};
|
||||
#endif //WAVE_H
|
||||
|
||||
void writeWave(const char* filename, int freq, int channels, short* buffer, int samples);
|
||||
|
||||
//this reads a 16 bit 1 or 2 channel wave file. returns 0 on error, 1 on success
|
||||
int readWave(const char* filename, int* freq, int* channels, short** buffer, int* samples);
|
||||
Binary file not shown.
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* TeamSpeak SDK client minimal 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 <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/clientlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#define strdup(x) _strdup(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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 clientlib_publicdefinitions.h
|
||||
* errorNumber - Error code. Should be zero when connecting or actively disconnection.
|
||||
* Contains error state when losing connection.
|
||||
*/
|
||||
void onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
|
||||
printf("Connect status changed: %llu %d %u\n", (unsigned long long)serverConnectionHandlerID, newStatus, errorNumber);
|
||||
/* Failed to connect ? */
|
||||
if(newStatus == STATUS_DISCONNECTED && errorNumber == ERROR_failed_connection_initialisation) {
|
||||
printf("Looks like there is no server running.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for current channels being announced to the client after connecting to a server.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the announced channel
|
||||
* channelParentID - ID of the parent channel
|
||||
*/
|
||||
void onNewChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID) {
|
||||
/* Query channel name from channel ID */
|
||||
char* name;
|
||||
unsigned int error;
|
||||
|
||||
printf("onNewChannelEvent: %llu %llu %llu\n", (unsigned long long)serverConnectionHandlerID, (unsigned long long)channelID, (unsigned long long)channelParentID);
|
||||
if((error = ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name)) == ERROR_ok) {
|
||||
printf("New channel: %llu %s \n", (unsigned long long)channelID, name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
} else {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error getting channel name in onNewChannelEvent: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for just created channels.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the announced channel
|
||||
* channelParentID - ID of the parent channel
|
||||
* invokerID - ID of the client who created the channel
|
||||
* invokerName - Name of the client who created the channel
|
||||
*/
|
||||
void onNewChannelCreatedEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
|
||||
char* name;
|
||||
|
||||
/* Query channel name from channel ID */
|
||||
if(ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name) != ERROR_ok)
|
||||
return;
|
||||
printf("New channel created: %s\n", name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when a channel was deleted.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - ID of the deleted channel
|
||||
* invokerID - ID of the client who deleted the channel
|
||||
* invokerName - Name of the client who deleted the channel
|
||||
*/
|
||||
void onDelChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
|
||||
printf("Channel ID %llu deleted by %s (%u)\n", (unsigned long long)channelID, invokerName, invokerID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a client joins, leaves or moves to another channel.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the moved client
|
||||
* oldChannelID - ID of the old channel left by the client
|
||||
* newChannelID - ID of the new channel joined by the client
|
||||
* visibility - Visibility of the moved client. See the enum Visibility in clientlib_publicdefinitions.h
|
||||
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
|
||||
*/
|
||||
void onClientMoveEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* moveMessage) {
|
||||
printf("ClientID %u moves from channel %llu to %llu with message %s\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, moveMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for other clients in current and subscribed channels being announced to the client.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the announced client
|
||||
* oldChannelID - ID of the subscribed channel where the client left visibility
|
||||
* newChannelID - ID of the subscribed channel where the client entered visibility
|
||||
* visibility - Visibility of the announced client. See the enum Visibility in clientlib_publicdefinitions.h
|
||||
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
|
||||
*/
|
||||
void onClientMoveSubscriptionEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility) {
|
||||
char* name;
|
||||
|
||||
/* Query client nickname from ID */
|
||||
if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
|
||||
return;
|
||||
printf("New client: %s\n", name);
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a client drops his connection.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the moved client
|
||||
* oldChannelID - ID of the channel the leaving client was previously member of
|
||||
* newChannelID - 0, as client is leaving
|
||||
* visibility - Always LEAVE_VISIBILITY
|
||||
* timeoutMessage - Optional message giving the reason for the timeout
|
||||
*/
|
||||
void onClientMoveTimeoutEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* timeoutMessage) {
|
||||
printf("ClientID %u timeouts with message %s\n",clientID, timeoutMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
void onTalkStatusChangeEvent(uint64 serverConnectionHandlerID, int status, int isReceivedWhisper, anyID clientID) {
|
||||
char* name;
|
||||
|
||||
/* Query client nickname from ID */
|
||||
if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
|
||||
return;
|
||||
if(status == STATUS_TALKING) {
|
||||
printf("Client \"%s\" starts talking.\n", name);
|
||||
} else {
|
||||
printf("Client \"%s\" stops talking.\n", name);
|
||||
}
|
||||
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when another client starts whispering to own client. Own client can decide to accept or deny
|
||||
* receiving the whisper by adding the sending client to the whisper allow list. If not added, whispering is blocked.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the whispering client
|
||||
*/
|
||||
void onIgnoredWhisperEvent(uint64 serverConnectionHandlerID, anyID clientID) {
|
||||
unsigned int error;
|
||||
|
||||
/* Add sending client to whisper allow list so own client will hear the voice data.
|
||||
* It is sufficient to add a clientID only once, not everytime this event is called. However it won't
|
||||
* hurt to add the same clientID to the allow list repeatedly, but is is not necessary. */
|
||||
if((error = ts3client_allowWhispersFrom(serverConnectionHandlerID, clientID)) != ERROR_ok) {
|
||||
printf("Error setting client on whisper allow list: %u\n", error);
|
||||
} else {
|
||||
printf("Added client %d to whisper allow list\n", clientID);
|
||||
}
|
||||
}
|
||||
|
||||
void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
|
||||
printf("Error for server %llu: %s %s\n", (unsigned long long)serverConnectionHandlerID, errorMessage, extraMessage);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64 scHandlerID;
|
||||
unsigned int error;
|
||||
char *version;
|
||||
char *identity;
|
||||
char * 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;
|
||||
funcs.onNewChannelEvent = onNewChannelEvent;
|
||||
funcs.onNewChannelCreatedEvent = onNewChannelCreatedEvent;
|
||||
funcs.onDelChannelEvent = onDelChannelEvent;
|
||||
funcs.onClientMoveEvent = onClientMoveEvent;
|
||||
funcs.onClientMoveSubscriptionEvent = onClientMoveSubscriptionEvent;
|
||||
funcs.onClientMoveTimeoutEvent = onClientMoveTimeoutEvent;
|
||||
funcs.onTalkStatusChangeEvent = onTalkStatusChangeEvent;
|
||||
funcs.onServerErrorEvent = onServerErrorEvent;
|
||||
funcs.onIgnoredWhisperEvent = onIgnoredWhisperEvent;
|
||||
|
||||
/* Initialize client lib with callbacks */
|
||||
/* Resource path points to the SDK\bin directory to locate the soundbackends*/
|
||||
path = programPath(argv[0]);
|
||||
error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE, NULL, path);
|
||||
free(path);
|
||||
|
||||
if(error != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initializing serverlib: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Spawn a new server connection handler using the default port and store the server ID */
|
||||
if((error = ts3client_spawnNewServerConnectionHandler(0, &scHandlerID)) != ERROR_ok) {
|
||||
printf("Error spawning server connection handler: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open default capture device */
|
||||
/* Passing empty string for mode and NULL or empty string for device will open the default device */
|
||||
if((error = ts3client_openCaptureDevice(scHandlerID, "", NULL)) != ERROR_ok) {
|
||||
printf("Error opening capture device: %d\n", error);
|
||||
}
|
||||
|
||||
/* Adjust "vad" preprocessor value to use vad by default */
|
||||
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "vad", "true")) != ERROR_ok) {
|
||||
printf("Error toggling VAD: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Adjust "vad_mode" value to use hybrid by default */
|
||||
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "vad_mode", "2")) != ERROR_ok) {
|
||||
printf("Error setting vad_mode value to hybrid: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Adjust "voiceactivation_level" value */
|
||||
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "voiceactivation_level", "-20")) != ERROR_ok) {
|
||||
printf("Error setting voiceactivation_level: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open default playback device */
|
||||
/* Passing empty string for mode and NULL or empty string for device will open the default device */
|
||||
if((error = ts3client_openPlaybackDevice(scHandlerID, "", NULL)) != ERROR_ok) {
|
||||
printf("Error opening playback device: %d\n", error);
|
||||
}
|
||||
|
||||
/* Create a new client identity */
|
||||
/* In your real application you should do this only once, store the assigned identity locally and then reuse it. */
|
||||
if((error = ts3client_createIdentity(&identity)) != ERROR_ok) {
|
||||
printf("Error creating identity: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
|
||||
if((error = ts3client_startConnection(scHandlerID, identity, "localhost", 9987, "client", NULL, "", "secret")) != ERROR_ok) {
|
||||
printf("Error connecting to server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ts3client_freeMemory(identity); /* Release dynamically allocated memory */
|
||||
identity = NULL;
|
||||
|
||||
printf("Client lib initialized and running\n");
|
||||
|
||||
/* Query and print client lib version */
|
||||
if((error = ts3client_getClientLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Failed to get clientlib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Client lib version: %s\n", version);
|
||||
ts3client_freeMemory(version); /* Release dynamically allocated memory */
|
||||
version = NULL;
|
||||
|
||||
SLEEP(500);
|
||||
|
||||
/* Wait for user input */
|
||||
printf("\n--- Press Return to disconnect from server and exit ---\n");
|
||||
getchar();
|
||||
|
||||
/* Disconnect from server. ERROR_not_connected just means the peer is already
|
||||
gone (e.g. the server was shut down) - nothing to do, so move on. */
|
||||
if((error = ts3client_stopConnection(scHandlerID, "leaving")) != ERROR_ok && error != ERROR_not_connected)
|
||||
printf("Error stopping connection: %d\n", error);
|
||||
|
||||
SLEEP(200);
|
||||
|
||||
/* Destroy server connection handler */
|
||||
if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok)
|
||||
printf("Error destroying server connection handler: %d\n", error);
|
||||
|
||||
/* Shutdown client lib */
|
||||
if((error = ts3client_destroyClientLib()) != ERROR_ok)
|
||||
printf("Failed to destroy clientlib: %d\n", error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* TeamSpeak SDK client minimal sample for filetransfer
|
||||
*
|
||||
* 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 <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/clientlib.h>
|
||||
|
||||
#define DEFAULT_VIRTUAL_SERVER 1
|
||||
#define NAME_BUFSIZE 1024
|
||||
#define CHANNEL_PASSWORD_BUFSIZE 1024
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#define strdup(x) _strdup(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
char* gProgramPath;
|
||||
|
||||
void emptyInputBuffer() {
|
||||
int c;
|
||||
while((c = getchar()) != '\n' && c != EOF);
|
||||
}
|
||||
|
||||
uint64 enterChannelID() {
|
||||
uint64 channelID;
|
||||
int n;
|
||||
|
||||
printf("\nEnter channel ID: ");
|
||||
n = scanf("%llu", (unsigned long long*)&channelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
return channelID;
|
||||
}
|
||||
|
||||
anyID enterTransferID() {
|
||||
anyID transferID;
|
||||
int n;
|
||||
|
||||
printf("\nEnter transferID: ");
|
||||
n = scanf("%hu", (anyID*)&transferID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
return transferID;
|
||||
}
|
||||
|
||||
uint64 enterBWLimit(const char* section) {
|
||||
uint64 limit;
|
||||
int n;
|
||||
|
||||
printf("Enter bandwidth limit for %s (or u for unlimited): ", section);
|
||||
n = scanf("%llu", (unsigned long long*)&limit);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
return BANDWIDTH_LIMIT_UNLIMITED;
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
void enterName(const char* text, char *name) {
|
||||
char *s;
|
||||
printf("\n%s: ", text);
|
||||
fgets(name, NAME_BUFSIZE, stdin);
|
||||
s = strrchr(name, '\n');
|
||||
if(s) {
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void enterPassword(char *password) {
|
||||
char *s;
|
||||
printf("\nEnter password: ");
|
||||
fgets(password, CHANNEL_PASSWORD_BUFSIZE, stdin);
|
||||
s = strrchr(password, '\n');
|
||||
if(s) {
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 clientlib_publicdefinitions.h
|
||||
* errorNumber - Error code. Should be zero when connecting or actively disconnection.
|
||||
* Contains error state when losing connection.
|
||||
*/
|
||||
void onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
|
||||
printf("Connect status changed: %llu %d %u\n", (unsigned long long)serverConnectionHandlerID, newStatus, errorNumber);
|
||||
/* Failed to connect ? */
|
||||
if(newStatus == STATUS_DISCONNECTED && errorNumber == ERROR_failed_connection_initialisation) {
|
||||
printf("Looks like there is no server running, terminate!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when another client starts whispering to own client. Own client can decide to accept or deny
|
||||
* receiving the whisper by adding the sending client to the whisper allow list. If not added, whispering is blocked.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* clientID - ID of the whispering client
|
||||
*/
|
||||
void onIgnoredWhisperEvent(uint64 serverConnectionHandlerID, anyID clientID) {
|
||||
unsigned int error;
|
||||
|
||||
/* Add sending client to whisper allow list so own client will hear the voice data.
|
||||
* It is sufficient to add a clientID only once, not everytime this event is called. However it won't
|
||||
* hurt to add the same clientID to the allow list repeatedly, but is is not necessary. */
|
||||
if((error = ts3client_allowWhispersFrom(serverConnectionHandlerID, clientID)) != ERROR_ok) {
|
||||
printf("Error setting client on whisper allow list: %u\n", error);
|
||||
} else {
|
||||
printf("Added client %d to whisper allow list\n", clientID);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when the server determines an error.
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* errorMessage - String containing a verbose error message
|
||||
* error - Error code as explained on public_errors.h
|
||||
* returnCode - Return code if it has been set by the Client Lib function call which caused this error event
|
||||
* extraMessage - Can contain additional information about the occurred error, otherwise it is an empty string
|
||||
*/
|
||||
|
||||
void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
|
||||
printf("Error for server %llu: %s %s\n", (unsigned long long)serverConnectionHandlerID, errorMessage, extraMessage ? extraMessage : "");
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is periodically called when a filetransfer is active
|
||||
*
|
||||
* Parameters:
|
||||
* transferID - Transfer ID for which filetransfer this event was called
|
||||
* status - Filetransfer status code as explained on public_errors.h
|
||||
* statusMessage - String containing a verbose status message
|
||||
* remotefileSize - Size in bytes of the remote file
|
||||
* scHandlerID - Server connection handler ID
|
||||
*/
|
||||
|
||||
void onFileTransferStatusEvent(anyID transferID, unsigned int status, const char* statusMessage, uint64 remotefileSize, uint64 scHandlerID) {
|
||||
printf("onFileTransferStatusEvent transferID: %d status: %d statusMessage: %s\n", transferID, status, statusMessage);
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when the fileList was requested (ts3client_requestFileList)
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - To which channel ID the remote file contains
|
||||
* path - Path of the remote file or directory
|
||||
* name - Name of the remote file or directory
|
||||
* size - Size in bytes of the remote file. If it is a directory this value is 0
|
||||
* datetime - Timestamp of the remote file or directory
|
||||
* type - Type of the remote item (0 is a folder or 1 is a file)
|
||||
* incompleteSize - If the file is not completely uploaded yet, this value contains the current available size
|
||||
* returnCode - Return code if it has been set by the Client Lib function call which caused this error event
|
||||
*/
|
||||
|
||||
void onFileListEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* path, const char* name, uint64 size, uint64 datetime, int type, uint64 incompletesize, const char* returnCode) {
|
||||
printf("onFileListEvent channelID: %llu path: %s filename: %s type:%s\n", channelID, path, name, type == 1 ? "file" : "dir");
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when the fileList request is finished
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - Server has finished sending events regarding this channel ID
|
||||
* path - Path of the remote directory
|
||||
*/
|
||||
|
||||
void onFileListFinishedEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* path) {
|
||||
printf("onFileListFinishedEvent: %llu\n", (unsigned long long)channelID);
|
||||
}
|
||||
|
||||
/*
|
||||
* This event is called when a requestFileInfo call was completed on the server
|
||||
*
|
||||
* Parameters:
|
||||
* serverConnectionHandlerID - Server connection handler ID
|
||||
* channelID - Server has finished sending events regarding this channel ID
|
||||
* name - name of the file
|
||||
* size - size in bytes of the file
|
||||
* datetime - file date/time in unix time
|
||||
*/
|
||||
|
||||
void onFileInfoEvent(uint64 serverConnectionHandlerID, uint64 channelID, const char* name, uint64 size, uint64 datetime){
|
||||
printf("onFileInfoEvent channelID: %llu filename: %s size:%llu date:%llu\n", channelID, name, size, datetime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print all channels of the given virtual server
|
||||
*/
|
||||
void showChannels(uint64 serverConnectionHandlerID) {
|
||||
uint64 *ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of channels on virtual server %llu:\n", (unsigned long long)serverConnectionHandlerID);
|
||||
if((error = ts3client_getChannelList(serverConnectionHandlerID, &ids)) != ERROR_ok) { /* Get array of channel IDs */
|
||||
printf("Error getting channel list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No channels\n\n");
|
||||
ts3client_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
if((error = ts3client_getChannelVariableAsString(serverConnectionHandlerID, ids[i], CHANNEL_NAME, &name)) != ERROR_ok) { /* Query channel name */
|
||||
printf("Error getting channel name: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("%llu - %s\n", (unsigned long long)ids[i], name);
|
||||
ts3client_freeMemory(name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3client_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void showChannelDir(uint64 serverConnectionHandlerID) {
|
||||
unsigned int error;
|
||||
uint64 channelID = enterChannelID();
|
||||
if(channelID) {
|
||||
if((error = ts3client_requestFileList(serverConnectionHandlerID, channelID, "", "/", "")) != ERROR_ok) { /* Requesting the root directory of this channel ID */
|
||||
printf("Error getting channel dir: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uploadFile(uint64 serverConnectionHandlerID) {
|
||||
anyID transferID;
|
||||
int overwrite = 1;
|
||||
int resume = 0;
|
||||
char filename[NAME_BUFSIZE];
|
||||
uint64 channelID = enterChannelID();
|
||||
printf("Uploading from predefined path: %s\n", gProgramPath);
|
||||
enterName("Enter filename to upload (<serverPath><filename> like /testfile.txt)", filename);
|
||||
if(channelID) {
|
||||
if(ts3client_sendFile(serverConnectionHandlerID, channelID, "", filename, overwrite, resume, gProgramPath, &transferID, NULL) == ERROR_ok) {
|
||||
printf("Sending file with transferID: %d\n", transferID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void downloadFile(uint64 serverConnectionHandlerID) {
|
||||
anyID transferID;
|
||||
int overwrite = 1;
|
||||
int resume = 0;
|
||||
uint64 channelID;
|
||||
char filename[NAME_BUFSIZE];
|
||||
channelID = enterChannelID();
|
||||
printf("Downloading in predefined path: %s\n", gProgramPath);
|
||||
enterName("Enter filename to download (<serverPath><filename> like /testfile.txt)", filename);
|
||||
|
||||
if(ts3client_requestFile(serverConnectionHandlerID, channelID, "", filename, overwrite, resume, gProgramPath, &transferID, NULL) == ERROR_ok) {
|
||||
printf("Recieving file with transferID: %d\n", transferID);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(uint64 serverConnectionHandlerID) {
|
||||
unsigned int error;
|
||||
uint64 channelID;
|
||||
char* files[2];
|
||||
char filename[NAME_BUFSIZE];
|
||||
channelID = enterChannelID();
|
||||
enterName("Enter filename to delete (<serverPath><filename> like /testfile.txt)", filename);
|
||||
|
||||
files[0] = filename;
|
||||
files[1] = 0;
|
||||
|
||||
if(channelID) {
|
||||
if((error = ts3client_requestDeleteFile(serverConnectionHandlerID, channelID, "", (const char**)files, NULL)) != ERROR_ok) {
|
||||
printf("Error deleting file: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renameFile(uint64 serverConnectionHandlerID) {
|
||||
unsigned int error;
|
||||
uint64 channelID;
|
||||
char oldName[NAME_BUFSIZE];
|
||||
char newName[NAME_BUFSIZE];
|
||||
|
||||
channelID = enterChannelID();
|
||||
enterName("Enter old name (<serverPath><filename> like /testfile.txt)", oldName);
|
||||
enterName("Enter new name (<serverPath><filename> like /new_testfile.txt)", newName);
|
||||
|
||||
if(channelID) {
|
||||
if((error = ts3client_requestRenameFile(serverConnectionHandlerID, channelID, "", channelID, "", oldName, newName, NULL)) != ERROR_ok) {
|
||||
printf("Error renaming file: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createDirectory(uint64 serverConnectionHandlerID) {
|
||||
unsigned int error;
|
||||
uint64 channelID;
|
||||
char dirName[NAME_BUFSIZE];
|
||||
|
||||
channelID = enterChannelID();
|
||||
enterName("Enter new directory name (<serverPath> like /subdir)", dirName);
|
||||
|
||||
if(channelID) {
|
||||
if((error = ts3client_requestCreateDirectory(serverConnectionHandlerID, channelID, "", dirName, NULL)) != ERROR_ok) {
|
||||
printf("Error renaming file: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fileInfo(uint64 serverConnectionHandlerID) {
|
||||
uint64 channelID;
|
||||
char filename[NAME_BUFSIZE];
|
||||
unsigned int error;
|
||||
|
||||
channelID = enterChannelID();
|
||||
enterName("Enter filename to get info on (<serverPath><filename> like /testfile.txt)", filename);
|
||||
|
||||
if((error=ts3client_requestFileInfo(serverConnectionHandlerID, channelID, "", filename, NULL)) != ERROR_ok) {
|
||||
printf("error getting file info: %d\n", error);
|
||||
}
|
||||
}
|
||||
|
||||
void bandwidth(uint64 serverConnectionHandlerID) {
|
||||
unsigned int error;
|
||||
uint64 instanceUpLimit;
|
||||
uint64 instanceDownLimit;
|
||||
uint64 schUpLimit;
|
||||
uint64 schDownLimit;
|
||||
|
||||
if((error=ts3client_getInstanceSpeedLimitUp(&instanceUpLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_getInstanceSpeedLimitUp: %d\n", error);
|
||||
instanceUpLimit=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getInstanceSpeedLimitDown(&instanceDownLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_getInstanceSpeedLimitDown: %d\n", error);
|
||||
instanceDownLimit=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getServerConnectionHandlerSpeedLimitUp(serverConnectionHandlerID, &schUpLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_getServerConnectionHandlerSpeedLimitUp: %d\n", error);
|
||||
schUpLimit=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getServerConnectionHandlerSpeedLimitDown(serverConnectionHandlerID, &schDownLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_getServerConnectionHandlerSpeedLimitDown: %d\n", error);
|
||||
schDownLimit=0;
|
||||
}
|
||||
|
||||
printf("current limits: instanceUp: %llu instanceDown: %llu schUp: %llu schDown: %llu\n", instanceUpLimit, instanceDownLimit, schUpLimit, schDownLimit);
|
||||
|
||||
instanceUpLimit = enterBWLimit("instanceUp");
|
||||
if((error=ts3client_setInstanceSpeedLimitUp(instanceUpLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_setInstanceSpeedLimitUp: %d\n", error);
|
||||
}
|
||||
|
||||
instanceDownLimit = enterBWLimit("instanceDown");
|
||||
if((error=ts3client_setInstanceSpeedLimitDown(instanceDownLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_setInstanceSpeedLimitDown: %d\n", error);
|
||||
}
|
||||
|
||||
schUpLimit = enterBWLimit("schUp");
|
||||
if((error=ts3client_setServerConnectionHandlerSpeedLimitUp(serverConnectionHandlerID, schUpLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_setServerConnectionHandlerSpeedLimitUp: %d\n", error);
|
||||
}
|
||||
|
||||
schDownLimit = enterBWLimit("schDown");
|
||||
if((error=ts3client_setServerConnectionHandlerSpeedLimitDown(serverConnectionHandlerID, schDownLimit)) != ERROR_ok){
|
||||
printf("error during ts3client_setServerConnectionHandlerSpeedLimitDown: %d\n", error);
|
||||
}
|
||||
}
|
||||
|
||||
void cancelTransfer(uint64 serverConnectionHandlerID){
|
||||
anyID transferID;
|
||||
unsigned int error;
|
||||
|
||||
transferID = enterTransferID();
|
||||
|
||||
if(transferID){
|
||||
if((error=ts3client_haltTransfer(serverConnectionHandlerID, transferID, 1, NULL))!=ERROR_ok){
|
||||
printf("error during ts3client_haltTransfer: %d\n", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transferStats(uint64 serverConnectionHandlerID){
|
||||
unsigned int error;
|
||||
uint64 bytesRecievedBandwidth;
|
||||
uint64 bytesSentBandwidth;
|
||||
uint64 bytesRecieved;
|
||||
uint64 bytesSent;
|
||||
|
||||
if((error=ts3client_getConnectionVariableAsUInt64(serverConnectionHandlerID, 0, CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, &bytesRecievedBandwidth))!=ERROR_ok){
|
||||
printf("error getting bytesRecievedBandwidth: %d\n", error);
|
||||
bytesRecievedBandwidth=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getConnectionVariableAsUInt64(serverConnectionHandlerID, 0, CONNECTION_FILETRANSFER_BANDWIDTH_SENT, &bytesSentBandwidth))!=ERROR_ok){
|
||||
printf("error getting bytesSentBandwidth: %d\n", error);
|
||||
bytesSentBandwidth=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getConnectionVariableAsUInt64(serverConnectionHandlerID, 0, CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, &bytesRecieved))!=ERROR_ok){
|
||||
printf("error getting bytesRecieved: %d\n", error);
|
||||
bytesRecieved=0;
|
||||
}
|
||||
|
||||
if((error=ts3client_getConnectionVariableAsUInt64(serverConnectionHandlerID, 0, CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, &bytesSent))!=ERROR_ok){
|
||||
printf("error getting bytesSent: %d\n", error);
|
||||
bytesSent=0;
|
||||
}
|
||||
|
||||
printf("Transfer statistics: BW recv: %llu BW send %llu bytes recv: %llu bytes sent: %llu\n", bytesRecievedBandwidth, bytesSentBandwidth, bytesRecieved, bytesSent);
|
||||
}
|
||||
|
||||
void showHelp() {
|
||||
printf("\n");
|
||||
printf("[q] - Disconnect from server\n");
|
||||
printf("[h] - Show this help\n");
|
||||
printf("[c] - Show channels\n");
|
||||
printf("[s] - Show directory of a channel\n");
|
||||
printf("[u] - Upload file\n");
|
||||
printf("[d] - Download file\n");
|
||||
printf("[x] - Delete file\n");
|
||||
printf("[r] - Rename file\n");
|
||||
printf("[f] - create directory file\n");
|
||||
printf("[i] - get file information\n");
|
||||
printf("[k] - cancel transfer\n");
|
||||
printf("[l] - edit transfer bandwidth limits\n");
|
||||
printf("[j] - get connection transfer stats\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint64 scHandlerID;
|
||||
unsigned int error;
|
||||
char *version;
|
||||
char *identity;
|
||||
short abort = 0;
|
||||
|
||||
/* 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;
|
||||
funcs.onServerErrorEvent = onServerErrorEvent;
|
||||
funcs.onFileListEvent = onFileListEvent;
|
||||
funcs.onFileListFinishedEvent = onFileListFinishedEvent;
|
||||
funcs.onFileTransferStatusEvent = onFileTransferStatusEvent; // crash when not defined!!!
|
||||
funcs.onFileInfoEvent = onFileInfoEvent;
|
||||
funcs.onIgnoredWhisperEvent = onIgnoredWhisperEvent;
|
||||
|
||||
/* Initialize client lib with callbacks */
|
||||
/* Resource path points to the SDK\bin directory to locate the soundbackends*/
|
||||
gProgramPath = programPath(argv[0]);
|
||||
error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, gProgramPath);
|
||||
|
||||
if(error != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Spawn a new server connection handler using the default port and store the server ID */
|
||||
if((error = ts3client_spawnNewServerConnectionHandler(0, &scHandlerID)) != ERROR_ok) {
|
||||
printf("Error spawning server connection handler: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create a new client identity */
|
||||
/* In your real application you should do this only once, store the assigned identity locally and then reuse it. */
|
||||
if((error = ts3client_createIdentity(&identity)) != ERROR_ok) {
|
||||
printf("Error creating identity: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
|
||||
if((error = ts3client_startConnection(scHandlerID, identity, "localhost", 9987, "client", NULL, "", "secret")) != ERROR_ok) {
|
||||
printf("Error connecting to server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ts3client_freeMemory(identity); /* Release dynamically allocated memory */
|
||||
identity = NULL;
|
||||
|
||||
printf("Client lib initialized and running\n");
|
||||
|
||||
/* Query and print client lib version */
|
||||
if((error = ts3client_getClientLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Failed to get clientlib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Client lib version: %s\n", version);
|
||||
ts3client_freeMemory(version); /* Release dynamically allocated memory */
|
||||
version = NULL;
|
||||
|
||||
SLEEP(500);
|
||||
|
||||
/* Simple commandline interface */
|
||||
printf("\nTeamSpeak 3 client commandline interface\n");
|
||||
showHelp();
|
||||
|
||||
/* Wait for user input */
|
||||
while(!abort) {
|
||||
int c = getc(stdin);
|
||||
switch(c) {
|
||||
case 'q':
|
||||
printf("\nDisconnecting from server...\n");
|
||||
abort = 1;
|
||||
break;
|
||||
case 'h':
|
||||
showHelp();
|
||||
break;
|
||||
case 'c':
|
||||
showChannels(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 's':
|
||||
showChannelDir(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'u':
|
||||
uploadFile(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'x':
|
||||
deleteFile(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'r':
|
||||
renameFile(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'd':
|
||||
downloadFile(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'f':
|
||||
createDirectory(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'i':
|
||||
fileInfo(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'k':
|
||||
cancelTransfer(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'l':
|
||||
bandwidth(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
case 'j':
|
||||
transferStats(DEFAULT_VIRTUAL_SERVER);
|
||||
break;
|
||||
}
|
||||
|
||||
SLEEP(50);
|
||||
}
|
||||
|
||||
/* Disconnect from server. ERROR_not_connected just means the peer is already
|
||||
gone (e.g. the server was shut down) - nothing to do, so move on. */
|
||||
if((error = ts3client_stopConnection(scHandlerID, "leaving")) != ERROR_ok && error != ERROR_not_connected)
|
||||
printf("Error stopping connection: %d\n", error);
|
||||
|
||||
SLEEP(200);
|
||||
|
||||
/* Destroy server connection handler */
|
||||
if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok)
|
||||
printf("Error destroying server connection handler: %d\n", error);
|
||||
|
||||
/* Shutdown client lib */
|
||||
if((error = ts3client_destroyClientLib()) != ERROR_ok)
|
||||
printf("Failed to destroy clientlib: %d\n", error);
|
||||
|
||||
free(gProgramPath);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* TeamSpeak SDK client multi-connection sample
|
||||
*
|
||||
* Spawns multiple simultaneous server connection handlers from a single
|
||||
* client application, all connecting to the same server. Intended to
|
||||
* reproduce/diagnose the issue where secondary sessions are dropped after
|
||||
* ~10 seconds with "10 resends of COMMAND packet" / ping timeout errors.
|
||||
*
|
||||
* Usage:
|
||||
* ts_client_multi [host] [port] [serverPassword] [numConnections] [runSeconds] [staggerMs] [shareIdentity] [localPort]
|
||||
*
|
||||
* Defaults: localhost 9987 "secret" 3 60 0 0 0
|
||||
*
|
||||
* localPort: local UDP port passed to spawnNewServerConnectionHandler.
|
||||
* 0 = ephemeral (a fresh port per connection handler). A fixed
|
||||
* value makes all connection handlers request the SAME local port,
|
||||
* mimicking applications that pin their source port.
|
||||
*
|
||||
* staggerMs: delay between issuing each connection (0 = all simultaneous).
|
||||
* Use a value larger than the handshake time (e.g. 2000) to start
|
||||
* each secondary session only after the previous one is fully
|
||||
* established, matching the reported "secondary sessions initiated
|
||||
* afterwards" scenario.
|
||||
*
|
||||
* 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 <time.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/clientlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#define strdup(x) _strdup(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep((x)*1000)
|
||||
#endif
|
||||
|
||||
#define MAX_CONNECTIONS 32
|
||||
|
||||
/* Track the connection handlers we created so the status callback can label them. */
|
||||
static uint64 g_handlers[MAX_CONNECTIONS];
|
||||
static int g_numHandlers = 0;
|
||||
static time_t g_startTime;
|
||||
|
||||
static int handlerIndex(uint64 scHandlerID) {
|
||||
for (int i = 0; i < g_numHandlers; ++i) {
|
||||
if (g_handlers[i] == scHandlerID) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static double elapsed(void) {
|
||||
return difftime(time(NULL), g_startTime);
|
||||
}
|
||||
|
||||
static const char* statusName(int status) {
|
||||
switch (status) {
|
||||
case STATUS_DISCONNECTED: return "DISCONNECTED";
|
||||
case STATUS_CONNECTING: return "CONNECTING";
|
||||
case STATUS_CONNECTED: return "CONNECTED";
|
||||
case STATUS_CONNECTION_ESTABLISHING:return "CONNECTION_ESTABLISHING";
|
||||
case STATUS_CONNECTION_ESTABLISHED: return "CONNECTION_ESTABLISHED";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection status change. This is where we expect to observe the secondary
|
||||
* connections dropping back to DISCONNECTED with a non-zero error after ~10s.
|
||||
*/
|
||||
void onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
|
||||
int idx = handlerIndex(serverConnectionHandlerID);
|
||||
char* errormsg = NULL;
|
||||
const char* errstr = "";
|
||||
if (errorNumber != ERROR_ok && ts3client_getErrorMessage(errorNumber, &errormsg) == ERROR_ok)
|
||||
errstr = errormsg;
|
||||
|
||||
printf("[%6.1fs] conn#%d (sch=%llu): %s (err=%u %s)\n",
|
||||
elapsed(), idx, (unsigned long long)serverConnectionHandlerID,
|
||||
statusName(newStatus), errorNumber, errstr);
|
||||
fflush(stdout);
|
||||
|
||||
if (errormsg) ts3client_freeMemory(errormsg);
|
||||
}
|
||||
|
||||
void onClientMoveTimeoutEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* timeoutMessage) {
|
||||
int idx = handlerIndex(serverConnectionHandlerID);
|
||||
printf("[%6.1fs] conn#%d: clientID %u timed out: %s\n", elapsed(), idx, clientID, timeoutMessage ? timeoutMessage : "");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
|
||||
int idx = handlerIndex(serverConnectionHandlerID);
|
||||
printf("[%6.1fs] conn#%d: server error: %s %s\n", elapsed(), idx, errorMessage ? errorMessage : "", extraMessage ? extraMessage : "");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
unsigned int error;
|
||||
char* version;
|
||||
char* path;
|
||||
|
||||
/* --- arguments --- */
|
||||
const char* host = (argc > 1) ? argv[1] : "localhost";
|
||||
unsigned int port = (argc > 2) ? (unsigned int)atoi(argv[2]) : 9987;
|
||||
const char* serverPassword = (argc > 3) ? argv[3] : "secret";
|
||||
int numConnections = (argc > 4) ? atoi(argv[4]) : 3;
|
||||
int runSeconds = (argc > 5) ? atoi(argv[5]) : 60;
|
||||
int staggerMs = (argc > 6) ? atoi(argv[6]) : 0;
|
||||
int shareIdentity = (argc > 7) ? atoi(argv[7]) : 0;
|
||||
int localPort = (argc > 8) ? atoi(argv[8]) : 0;
|
||||
|
||||
if (numConnections < 1) numConnections = 1;
|
||||
if (numConnections > MAX_CONNECTIONS) numConnections = MAX_CONNECTIONS;
|
||||
|
||||
printf("Multi-connection test: host=%s port=%u connections=%d runSeconds=%d staggerMs=%d shareIdentity=%d localPort=%d\n",
|
||||
host, port, numConnections, runSeconds, staggerMs, shareIdentity, localPort);
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ClientUIFunctions funcs;
|
||||
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
|
||||
funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;
|
||||
funcs.onClientMoveTimeoutEvent = onClientMoveTimeoutEvent;
|
||||
funcs.onServerErrorEvent = onServerErrorEvent;
|
||||
|
||||
/* Initialize client lib with callbacks. Resource path points to the dir
|
||||
* containing the soundbackends (next to the executable). */
|
||||
path = programPath(argv[0]);
|
||||
error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE, NULL, path);
|
||||
free(path);
|
||||
|
||||
if (error != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if (ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initializing clientlib: %s\n", errormsg);
|
||||
ts3client_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((error = ts3client_getClientLibVersion(&version)) == ERROR_ok) {
|
||||
printf("Client lib version: %s\n", version);
|
||||
ts3client_freeMemory(version);
|
||||
}
|
||||
|
||||
g_startTime = time(NULL);
|
||||
|
||||
/* When shareIdentity is set, all connections reuse a single identity (as a
|
||||
* real application that stores and reuses one identity would). This mirrors
|
||||
* "multiple sessions from the same client" and is a prime suspect for the
|
||||
* handshake hardening rejecting secondary sessions from the same uid. */
|
||||
char* sharedIdentity = NULL;
|
||||
if (shareIdentity) {
|
||||
if ((error = ts3client_createIdentity(&sharedIdentity)) != ERROR_ok) {
|
||||
printf("Error creating shared identity: %u\n", error);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Spawn N connection handlers and connect each to the same server.
|
||||
* No audio devices are opened: this keeps the test focused on the
|
||||
* network/command layer and avoids capture-device contention. */
|
||||
for (int i = 0; i < numConnections; ++i) {
|
||||
uint64 scHandlerID;
|
||||
char* identity = NULL;
|
||||
char nickname[64];
|
||||
|
||||
if ((error = ts3client_spawnNewServerConnectionHandler(localPort, &scHandlerID)) != ERROR_ok) {
|
||||
printf("Error spawning server connection handler #%d: %u\n", i, error);
|
||||
continue;
|
||||
}
|
||||
g_handlers[g_numHandlers++] = scHandlerID;
|
||||
|
||||
if (shareIdentity) {
|
||||
identity = sharedIdentity;
|
||||
} else if ((error = ts3client_createIdentity(&identity)) != ERROR_ok) {
|
||||
printf("Error creating identity #%d: %u\n", i, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(nickname, sizeof(nickname), "multi_%d", i);
|
||||
|
||||
if ((error = ts3client_startConnection(scHandlerID, identity, host, port, nickname, NULL, "", serverPassword)) != ERROR_ok) {
|
||||
printf("Error connecting conn#%d to server: %u\n", i, error);
|
||||
} else {
|
||||
printf("[%6.1fs] conn#%d (sch=%llu): startConnection issued as \"%s\"%s\n",
|
||||
elapsed(), i, (unsigned long long)scHandlerID, nickname,
|
||||
shareIdentity ? " [shared identity]" : "");
|
||||
}
|
||||
if (!shareIdentity && identity)
|
||||
ts3client_freeMemory(identity);
|
||||
|
||||
/* Optionally wait before starting the next connection, so secondary
|
||||
* sessions are initiated only after the previous one is up. */
|
||||
if (staggerMs > 0 && i + 1 < numConnections) {
|
||||
SLEEP(staggerMs);
|
||||
}
|
||||
}
|
||||
|
||||
if (sharedIdentity) ts3client_freeMemory(sharedIdentity);
|
||||
|
||||
printf("\n--- %d connection(s) started. Observing for %d seconds. ---\n", g_numHandlers, runSeconds);
|
||||
printf("--- Watch for secondary connections dropping (resend/ping timeout). ---\n\n");
|
||||
fflush(stdout);
|
||||
|
||||
/* Idle loop, printing a periodic status snapshot so drops are easy to spot. */
|
||||
for (int t = 0; t < runSeconds; ++t) {
|
||||
SLEEP(1000);
|
||||
if ((t + 1) % 5 == 0) {
|
||||
printf("[%6.1fs] status snapshot:\n", elapsed());
|
||||
for (int i = 0; i < g_numHandlers; ++i) {
|
||||
int status = 0;
|
||||
ts3client_getConnectionStatus(g_handlers[i], &status);
|
||||
printf(" conn#%d (sch=%llu): %s\n", i, (unsigned long long)g_handlers[i], statusName(status));
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disconnect and clean up all connection handlers. */
|
||||
printf("\nDisconnecting all connections...\n");
|
||||
for (int i = 0; i < g_numHandlers; ++i) {
|
||||
ts3client_stopConnection(g_handlers[i], "leaving");
|
||||
}
|
||||
SLEEP(500);
|
||||
for (int i = 0; i < g_numHandlers; ++i) {
|
||||
ts3client_destroyServerConnectionHandler(g_handlers[i]);
|
||||
}
|
||||
|
||||
if ((error = ts3client_destroyClientLib()) != ERROR_ok) {
|
||||
printf("Failed to destroy clientlib: %u\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
# Collect a small set of SDK headers to show in the IDE solution.
|
||||
# Depends on find_package(team_client|team_server) having run.
|
||||
|
||||
if("${sample_type}" STREQUAL "client")
|
||||
get_target_property(_ts_sdk_inc_dir teamspeak::client INTERFACE_INCLUDE_DIRECTORIES)
|
||||
elseif("${sample_type}" STREQUAL "server")
|
||||
get_target_property(_ts_sdk_inc_dir teamspeak::server INTERFACE_INCLUDE_DIRECTORIES)
|
||||
else()
|
||||
set(_ts_sdk_inc_dir "")
|
||||
endif()
|
||||
|
||||
set(TS_SDK_IDE_FILES "")
|
||||
if(_ts_sdk_inc_dir)
|
||||
file(GLOB _ts_log_headers "${_ts_sdk_inc_dir}/teamlog/*.h")
|
||||
list(APPEND TS_SDK_IDE_FILES
|
||||
${_ts_log_headers}
|
||||
"${_ts_sdk_inc_dir}/teamspeak/public_definitions.h"
|
||||
"${_ts_sdk_inc_dir}/teamspeak/public_errors.h"
|
||||
)
|
||||
if("${sample_type}" STREQUAL "client")
|
||||
list(APPEND TS_SDK_IDE_FILES "${_ts_sdk_inc_dir}/teamspeak/clientlib.h")
|
||||
elseif("${sample_type}" STREQUAL "server")
|
||||
list(APPEND TS_SDK_IDE_FILES
|
||||
"${_ts_sdk_inc_dir}/teamspeak/server_commands_file_transfer.h"
|
||||
"${_ts_sdk_inc_dir}/teamspeak/serverlib.h"
|
||||
"${_ts_sdk_inc_dir}/teamspeak/serverlib_publicdefinitions.h"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,10 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(triple aarch64-linux-gnueabi)
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_C_COMPILER_TARGET ${triple})
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_CXX_COMPILER_TARGET ${triple})
|
||||
set(CMAKE_SYSROOT /usr/aarch64-linux-gnu)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=/usr/aarch64-linux-gnu/bin/ld" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_AR /usr/aarch64-linux-gnu/bin/ar CACHE FILEPATH "" FORCE)
|
||||
@@ -0,0 +1,5 @@
|
||||
pushd build
|
||||
pushd win_x64
|
||||
cmake -G "Visual Studio 17 2022" -A x64 ../..
|
||||
popd
|
||||
popd
|
||||
@@ -0,0 +1,8 @@
|
||||
pushd build
|
||||
rm -rf linux_x64
|
||||
mkdir linux_x64
|
||||
pushd linux_x64
|
||||
cmake -G Ninja ../..
|
||||
ninja
|
||||
popd
|
||||
popd
|
||||
@@ -0,0 +1,8 @@
|
||||
pushd build
|
||||
rm -rf mac
|
||||
mkdir mac
|
||||
pushd mac
|
||||
cmake -G Ninja ../..
|
||||
ninja
|
||||
popd
|
||||
popd
|
||||
@@ -0,0 +1,46 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include "id_io.h"
|
||||
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for reading keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fgets(keyPair, BUFSIZ, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error reading keypair from file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Read keypair '%s' from file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "w");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for writing keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(keyPair, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error writing keypair to file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Wrote keypair '%s' to file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef ID_IO_H
|
||||
#define ID_IO_H
|
||||
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair);
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* TeamSpeak SDK server sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/serverlib_publicdefinitions.h>
|
||||
#include <teamspeak/serverlib.h>
|
||||
#include "id_io.h"
|
||||
|
||||
#define DEFAULT_VIRTUAL_SERVER_ID 1
|
||||
|
||||
/* Maximum number of clients allowed per virtual server */
|
||||
#define MAX_CLIENTS 8
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
/* Enable to use server-side voice recording */
|
||||
/* #define USE_VOICEDATAEVENT */
|
||||
|
||||
/* Enable to use custom encryption */
|
||||
/* #define USE_CUSTOM_ENCRYPTION
|
||||
#define CUSTOM_CRYPT_KEY 123 */
|
||||
|
||||
/* Uncomment "#define CUSTOM_PASSWORDS" to try custom passwords.
|
||||
* Please note that you have to do the same on the client demo too */
|
||||
/* #define CUSTOM_PASSWORDS */
|
||||
|
||||
#ifdef USE_VOICEDATAEVENT
|
||||
#ifdef _WINDOWS
|
||||
#include <io.h>
|
||||
#else /* Unix compatibility */
|
||||
#include <unistd.h>
|
||||
#define _open open
|
||||
#define _write write
|
||||
#define _close close
|
||||
#define _O_CREAT O_CREAT
|
||||
#define _O_WRONLY O_WRONLY
|
||||
#define _S_IREAD S_IREAD
|
||||
#define _S_IWRITE S_IWRITE
|
||||
#define _O_APPEND O_APPEND
|
||||
#define _O_BINARY 0
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#define CHECK_ERROR(x) if((error = x) != ERROR_ok) { goto on_error; }
|
||||
|
||||
/*
|
||||
* Callback when client has connected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of connected client
|
||||
* channelID - ID of channel the client joined
|
||||
*/
|
||||
void onClientConnected(uint64 serverID, anyID clientID, uint64 channelID, unsigned int* removeClientError) {
|
||||
char* clientName;
|
||||
unsigned int error;
|
||||
|
||||
/* Query client nickname */
|
||||
if((error = ts3server_getClientVariableAsString(serverID, clientID, CLIENT_NICKNAME, &clientName)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying client nickname: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Client '%s' joined channel %llu on virtual server %llu\n", clientName, (unsigned long long) channelID, (unsigned long long)serverID);
|
||||
|
||||
/* Example: Kick clients with nickname "BlockMe from server */
|
||||
if(!strcmp(clientName, "BlockMe")) {
|
||||
printf("Blocking bad client!\n");
|
||||
*removeClientError = ERROR_client_not_logged_in; /* Give a reason */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has disconnected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of disconnected client
|
||||
* channelID - ID of channel the client left
|
||||
*/
|
||||
void onClientDisconnected(uint64 serverID, anyID clientID, uint64 channelID) {
|
||||
printf("Client %u left channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has moved.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of moved client
|
||||
* oldChannelID - ID of old channel the client left
|
||||
* newChannelID - ID of new channel the client joined
|
||||
*/
|
||||
void onClientMoved(uint64 serverID, anyID clientID, uint64 oldChannelID, uint64 newChannelID) {
|
||||
printf("Client %u moved from channel %llu to channel %llu on virtual server %llu\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been created.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who created the channel
|
||||
* channelID - ID of the created channel
|
||||
*/
|
||||
void onChannelCreated(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu created by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been edited.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who edited the channel
|
||||
* channelID - ID of the edited channel
|
||||
*/
|
||||
void onChannelEdited(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu edited by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been deleted.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who deleted the channel
|
||||
* channelID - ID of the deleted channel
|
||||
*/
|
||||
void onChannelDeleted(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu deleted by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when a server text message has been received.
|
||||
* Note that only server and channel chats are received, private client messages are not caught due to privacy reasons.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who sent the text message
|
||||
* textMessage - Message text
|
||||
*/
|
||||
void onServerTextMessageEvent(uint64 serverID, anyID invokerClientID, const char* textMessage) {
|
||||
char* invokerNickname;
|
||||
unsigned int error;
|
||||
|
||||
/* Get invoker nickname */
|
||||
if((error = ts3server_getClientVariableAsString(serverID, invokerClientID, CLIENT_NICKNAME, &invokerNickname)) != ERROR_ok) {
|
||||
printf("Error getting client nickname: %d\n", error);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Text message in server chat by %s: %s\n", invokerNickname, textMessage);
|
||||
|
||||
ts3server_freeMemory(invokerNickname); /* Release previously allocated memory */
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when a channel text message has been received.
|
||||
* Note that only server and channel chats are received, private client messages are not caught due to privacy reasons.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who sent the text message
|
||||
* targetChannelID - ID of the channel in which the chat was sent
|
||||
* textMessage - Message text
|
||||
*/
|
||||
void onChannelTextMessageEvent(uint64 serverID, anyID invokerClientID, uint64 targetChannelID, const char* textMessage) {
|
||||
char* invokerNickname;
|
||||
char* channelName;
|
||||
unsigned int error;
|
||||
|
||||
/* Get invoker nickname */
|
||||
if((error = ts3server_getClientVariableAsString(serverID, invokerClientID, CLIENT_NICKNAME, &invokerNickname)) != ERROR_ok) {
|
||||
printf("Error getting client nickname: %d\n", error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get channel name */
|
||||
if((error = ts3server_getChannelVariableAsString(serverID, targetChannelID, CHANNEL_NAME, &channelName)) != ERROR_ok) {
|
||||
printf("Error getting channel name: %d\n", error);
|
||||
ts3server_freeMemory(invokerNickname);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Text message in channel '%s' by %s: %s\n", channelName, invokerNickname, textMessage);
|
||||
|
||||
ts3server_freeMemory(invokerNickname);
|
||||
ts3server_freeMemory(channelName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for user-defined logging.
|
||||
*
|
||||
* Parameter:
|
||||
* logMessage - Log message text
|
||||
* logLevel - Severity of log message
|
||||
* logChannel - Custom text to categorize the message channel
|
||||
* logID - Virtual server ID giving the virtual server source of the log event
|
||||
* logTime - String with the date and time the log entry occured
|
||||
* completeLogString - Verbose log message including all previous parameters for convinience
|
||||
*/
|
||||
void onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
|
||||
/* Your custom error display here... */
|
||||
/* printf("LOG: %s\n", completeLogString); */
|
||||
if(logLevel == LogLevel_CRITICAL) {
|
||||
exit(1); /* Your custom handling of critical errors */
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_VOICEDATAEVENT
|
||||
/*
|
||||
* Callback triggered by the specified client sending voice data.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the server sending the callback
|
||||
* clientID - ID of the client sending the voice data
|
||||
* voiceData - Voice data buffer. Format is 16 bit mono. Do not free this buffer.
|
||||
* voiceDataSize - Size of the voiceData buffer
|
||||
* frequency - Voice frequency
|
||||
*/
|
||||
void onVoiceDataEvent(uint64 serverID, anyID clientID, unsigned char* voiceData, unsigned int voiceDataSize, unsigned int frequency) {
|
||||
int fd;
|
||||
unsigned int error;
|
||||
char* name;
|
||||
|
||||
/* Query client nickname as string */
|
||||
if((error = ts3server_getClientVariableAsString(serverID, clientID, CLIENT_NICKNAME, &name)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying client nickname: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open file with client nickname as filename and write voice data */
|
||||
fd =_open(name, _O_CREAT | _O_APPEND | _O_BINARY | _O_WRONLY , _S_IREAD | _S_IWRITE);
|
||||
if(fd == -1) {
|
||||
printf("failed to open file\n");
|
||||
ts3server_freeMemory(name);
|
||||
exit(-1);
|
||||
}
|
||||
_write(fd, voiceData, voiceDataSize);
|
||||
_close(fd);
|
||||
|
||||
/* Release string dynamically allocated in getClientVariableAsString */
|
||||
ts3server_freeMemory(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_PASSWORDS
|
||||
/*
|
||||
* Called to encrypt channel and server passwords
|
||||
*
|
||||
* In this example, we do not do any encryption. That way we have clear
|
||||
* text passwords on the server.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - Server ID
|
||||
* plaintext - the clear text password
|
||||
* encryptedText - pointer to a buffer to store the encrypted password
|
||||
* encryptedTextByteSize - the size of the encryptedText buffer
|
||||
*/
|
||||
void onClientPasswordEncrypt(uint64 serverID, const char* plaintext, char* encryptedText, int encryptedTextByteSize){
|
||||
int pt_len;
|
||||
|
||||
printf("onClientPasswordEncrypt called\n");
|
||||
|
||||
pt_len = strlen(plaintext);
|
||||
if (encryptedTextByteSize < pt_len-1) pt_len = encryptedTextByteSize-1;
|
||||
|
||||
memcpy(encryptedText, plaintext, pt_len);
|
||||
encryptedText[pt_len]=0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered for server password.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server to which the client is connecting.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is connecting to the server. Please view public_definitions.h.
|
||||
* password - Password provided by the client.
|
||||
*
|
||||
* Return ERROR_ok to indicate the password is correct. Return ERROR_server_invalid_password for incorrect.
|
||||
*/
|
||||
unsigned int onCustomServerPasswordCheck(uint64 serverID, const struct ClientMiniExport* client, const char* password){
|
||||
if (strcmp(password, "secret") == 0) return ERROR_ok;
|
||||
return ERROR_server_invalid_password;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered for channel password.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is moving itself or other clients to a new channel.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is moving itself or other clients to the channel. Please view public_definitions.h.
|
||||
* channelID - Channel ID of the channel being switched/moved to
|
||||
* password - Password provided by client.
|
||||
*
|
||||
* Return ERROR_ok to indicate the password is correct. Return ERROR_channel_invalid_password for incorrect.
|
||||
*/
|
||||
unsigned int onCustomChannelPasswordCheck(uint64 serverID, const struct ClientMiniExport* client, uint64 channelID, const char* password){
|
||||
if (strcmp(password, "channelpw") == 0) return ERROR_ok;
|
||||
return ERROR_channel_invalid_password;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client starts talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the server sending the callback
|
||||
* clientID - ID of the client which started talking
|
||||
*/
|
||||
void onClientStartTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStartTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client stops talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the server sending the callback
|
||||
* clientID - ID of the client which stopped talking
|
||||
*/
|
||||
void onClientStopTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStopTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when a license error occurs.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the license error occured. This virtual server will be automatically
|
||||
* shutdown. If the parameter is zero, all virtual servers are affected and have been shutdown.
|
||||
* errorCode - Code of the occured error. Use ts3server_getGlobalErrorMessage() to convert to a message string
|
||||
*/
|
||||
void onAccountingErrorEvent(uint64 serverID, unsigned int errorCode) {
|
||||
char* errorMessage;
|
||||
if(ts3server_getGlobalErrorMessage(errorCode, &errorMessage) == ERROR_ok) {
|
||||
printf("onAccountingErrorEvent serverID=%llu, errorCode=%u: %s\n", (unsigned long long)serverID, errorCode, errorMessage);
|
||||
ts3server_freeMemory(errorMessage);
|
||||
}
|
||||
|
||||
/* Your custom handling here. In a real application, you wouldn't stop the whole process because the TS3 server part went down.
|
||||
* The whole idea of this callback is to let you gracefully handle the TS3 server failing to start and to continue your application. */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback allowing to apply custom encryption to outgoing packets.
|
||||
* Using this function is optional. Do not implement if you want to handle the TeamSpeak SDK encryption.
|
||||
*
|
||||
* Parameters:
|
||||
* dataToSend - Pointer to an array with the outgoing data to be encrypted
|
||||
* sizeOfData - Pointer to an integer value containing the size of the data array
|
||||
*
|
||||
* Apply your custom encryption to the data array. If the encrypted data is smaller than sizeOfData, write
|
||||
* your encrypted data into the existing memory of dataToSend. If your encrypted data is larger, you need
|
||||
* to allocate memory and redirect the pointer dataToSend. You need to take care of freeing your own allocated
|
||||
* memory yourself. The memory allocated by the SDK, to which dataToSend is originally pointing to, must not
|
||||
* be freed.
|
||||
*
|
||||
*/
|
||||
void onCustomPacketEncryptEvent(char** dataToSend, unsigned int* sizeOfData) {
|
||||
#ifdef USE_CUSTOM_ENCRYPTION
|
||||
unsigned int i;
|
||||
for(i = 0; i < *sizeOfData; i++) {
|
||||
(*dataToSend)[i] ^= CUSTOM_CRYPT_KEY;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback allowing to apply custom encryption to incoming packets
|
||||
* Using this function is optional. Do not implement if you want to handle the TeamSpeak SDK encryption.
|
||||
*
|
||||
* Parameters:
|
||||
* dataToSend - Pointer to an array with the received data to be decrypted
|
||||
* sizeOfData - Pointer to an integer value containing the size of the data array
|
||||
*
|
||||
* Apply your custom decryption to the data array. If the decrypted data is smaller than dataReceivedSize, write
|
||||
* your decrypted data into the existing memory of dataReceived. If your decrypted data is larger, you need
|
||||
* to allocate memory and redirect the pointer dataReceived. You need to take care of freeing your own allocated
|
||||
* memory yourself. The memory allocated by the SDK, to which dataReceived is originally pointing to, must not
|
||||
* be freed.
|
||||
*/
|
||||
void onCustomPacketDecryptEvent(char** dataReceived, unsigned int* dataReceivedSize) {
|
||||
#ifdef USE_CUSTOM_ENCRYPTION
|
||||
unsigned int i;
|
||||
for(i = 0; i < *dataReceivedSize; i++) {
|
||||
(*dataReceived)[i] ^= CUSTOM_CRYPT_KEY;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void showHelp() {
|
||||
printf("\n[q] - Quit\n[h] - Show this help\n[v] - List virtual servers\n[c] - Show channels of virtual server %d\n", DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[l] - Show clients of virtual server %d\n[n] - Create new channel on virtual server %d with generated name\n[N] - Create new channel on virtual server %d with custom name\n", DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[d] - Delete channel on virtual server %d\n[r] - Rename channel on virtual server %d\n[m] - Move client on virtual server %d\n", DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[C] - Create new virtual server\n[E] - Edit virtual server\n[S] - Stop virtual server\n\n");
|
||||
}
|
||||
|
||||
void emptyInputBuffer() {
|
||||
int c;
|
||||
while((c = getchar()) != '\n' && c != EOF);
|
||||
}
|
||||
|
||||
void showVirtualServers() {
|
||||
uint64* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of virtual servers:\n");
|
||||
if((error = ts3server_getVirtualServerList(&ids)) != ERROR_ok) { /* Get array of virtual server IDs */
|
||||
printf("Error getting virtual server list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No virtual servers\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
int slotCount;
|
||||
char* virtualServerUniqueIdentifier;
|
||||
if((error = ts3server_getVirtualServerVariableAsString(ids[i], VIRTUALSERVER_NAME, &name)) != ERROR_ok) { /* Query server name */
|
||||
printf("Error getting virtual server nickname: %d\n", error);
|
||||
break;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsInt(ids[i], VIRTUALSERVER_MAXCLIENTS, &slotCount)) != ERROR_ok) {
|
||||
printf("Error getting virtual server slot count: %d\n", error);
|
||||
break;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsString(ids[i], VIRTUALSERVER_UNIQUE_IDENTIFIER, &virtualServerUniqueIdentifier)) != ERROR_ok) {
|
||||
printf("Error getting virtual server unique identifier: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("ID=%llu NAME=\"%s\" CAPACITY=%d Unique Identifier=\"%s\"\n", (unsigned long long)ids[i], name, slotCount, virtualServerUniqueIdentifier);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
ts3server_freeMemory(virtualServerUniqueIdentifier);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void showChannels(uint64 serverID) {
|
||||
uint64* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of channels on virtual server %llu:\n", (unsigned long long)serverID);
|
||||
if((error = ts3server_getChannelList(serverID, &ids)) != ERROR_ok) { /* Get array of channel IDs */
|
||||
printf("Error getting channel list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No channels\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
if((error = ts3server_getChannelVariableAsString(serverID, ids[i], CHANNEL_NAME, &name)) != ERROR_ok) { /* Query channel name */
|
||||
printf("Error querying channel name: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("%llu - %s\n", (unsigned long long)ids[i], name);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void showClients(uint64 serverID) {
|
||||
anyID* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of clients on virtual server %llu:\n", (unsigned long long)serverID);
|
||||
if((error = ts3server_getClientList(serverID, &ids)) != ERROR_ok) { /* Get array of client IDs */
|
||||
printf("Error getting client list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No clients\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
if((error = ts3server_getClientVariableAsString(serverID, ids[i], CLIENT_NICKNAME, &name)) != ERROR_ok) { /* Query client nickname */
|
||||
printf("Error querying client nickname: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("%u - %s\n", ids[i], name);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void createDefaultChannelName(char *name) {
|
||||
static int i = 1;
|
||||
sprintf(name, "Channel_%d", i++);
|
||||
}
|
||||
|
||||
void enterName(char *name) {
|
||||
char *s;
|
||||
printf("\nEnter name: ");
|
||||
fgets(name, BUFSIZ, stdin);
|
||||
s = strrchr(name, '\n');
|
||||
if(s) {
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void createChannel(uint64 serverID, const char *name) {
|
||||
unsigned int error;
|
||||
uint64 newChannelID;
|
||||
|
||||
/* Set data of new channel. Use channelID of 0 for creating channels. */
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsString(serverID, 0, CHANNEL_NAME, name));
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsString(serverID, 0, CHANNEL_TOPIC, "Test channel topic"));
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsString(serverID, 0, CHANNEL_DESCRIPTION, "Test channel description"));
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsInt (serverID, 0, CHANNEL_FLAG_PERMANENT, 1));
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsInt (serverID, 0, CHANNEL_FLAG_SEMI_PERMANENT, 0));
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsInt (serverID, 0, CHANNEL_CODEC_QUALITY, 10));
|
||||
|
||||
/* Flush changes to server */
|
||||
CHECK_ERROR(ts3server_flushChannelCreation(serverID, 0, &newChannelID)); /* Create as top-level channel */
|
||||
|
||||
printf("\nCreated channel: %llu\n\n", (unsigned long long)newChannelID);
|
||||
return;
|
||||
|
||||
on_error:
|
||||
printf("Error creating channel: %d\n", error);
|
||||
}
|
||||
|
||||
void deleteChannel(uint64 serverID) {
|
||||
uint64 channelID;
|
||||
int n;
|
||||
unsigned int error;
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to delete: ");
|
||||
n = scanf("%llu", (unsigned long long*)&channelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Delete channel */
|
||||
if((error = ts3server_channelDelete(serverID, channelID, 0)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error deleting channel: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renameChannel(uint64 serverID) {
|
||||
uint64 channelID;
|
||||
int n;
|
||||
unsigned int error;
|
||||
char name[BUFSIZ];
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to rename: ");
|
||||
n = scanf("%llu", (unsigned long long*)&channelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Query new channel name from user */
|
||||
enterName(name);
|
||||
|
||||
/* Change channel name and flush changes */
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsString(serverID, channelID, CHANNEL_NAME, name));
|
||||
CHECK_ERROR(ts3server_flushChannelVariable(serverID, channelID));
|
||||
|
||||
printf("Renamed channel %llu\n\n", (unsigned long long)channelID);
|
||||
return;
|
||||
|
||||
on_error:
|
||||
printf("Error renaming channel: %d\n\n", error);
|
||||
}
|
||||
|
||||
void moveClient(uint64 serverID) {
|
||||
anyID clientIDArray[2]; /* We only want to move one client plus terminating null end-marker */
|
||||
uint64 newChannelID; /* ID of channel to move the client into */
|
||||
unsigned int error;
|
||||
int n;
|
||||
|
||||
/* Query client ID from user */
|
||||
printf("\nEnter ID of client to move: ");
|
||||
n = scanf("%hu", &clientIDArray[0]);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
clientIDArray[1] = 0; /* Add end-marker */
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to move client into: ");
|
||||
n = scanf("%llu", (unsigned long long*)&newChannelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move client and check for error */
|
||||
if((error = ts3server_clientMove(serverID, newChannelID, clientIDArray)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error moving client: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Client %d moved to channel %llu\n", clientIDArray[0], (unsigned long long)newChannelID);
|
||||
}
|
||||
|
||||
uint64 createVirtualServer(const char* name, int port, unsigned int maxClients) {
|
||||
char buffer[BUFSIZ] = { 0 };
|
||||
char filename[BUFSIZ];
|
||||
char port_str[20];
|
||||
char *keyPair;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
|
||||
/* Assemble filename: keypair_<port>.txt */
|
||||
strcpy(filename, "keypair_");
|
||||
sprintf(port_str, "%d", port);
|
||||
strcat(filename, port_str);
|
||||
strcat(filename, ".txt");
|
||||
|
||||
/* Try reading keyPair from file */
|
||||
if(readKeyPairFromFile(filename, buffer) == 0) {
|
||||
keyPair = buffer; /* Id read from file */
|
||||
} else {
|
||||
keyPair = ""; /* No Id saved, start virtual server with empty keyPair string */
|
||||
}
|
||||
|
||||
/* Create the virtual server with specified port, name, keyPair and max clients */
|
||||
printf("Create virtual server using keypair '%s'\n", keyPair);
|
||||
printf("Create virtual server with %d slots\n", maxClients);
|
||||
//listen on any address on ipv4 and ipv6 (it is also possible to enter multiple ipv4 and ipv6 addresses here)
|
||||
if((error = ts3server_createVirtualServer(port, "0.0.0.0, ::", name, keyPair, maxClients, &serverID)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error creating virtual server: %s (%d)\n", errormsg, error);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we didn't load the keyPair before, query it from virtual server and save to file */
|
||||
if(!*buffer) {
|
||||
if((error = ts3server_getVirtualServerKeyPair(serverID, &keyPair)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying keyPair: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save keyPair to file "keypair_<port>.txt"*/
|
||||
if(writeKeyPairToFile(filename, keyPair) != 0) {
|
||||
ts3server_freeMemory(keyPair);
|
||||
return 0;
|
||||
}
|
||||
ts3server_freeMemory(keyPair);
|
||||
}
|
||||
|
||||
return serverID;
|
||||
}
|
||||
|
||||
uint64 startVirtualServer() {
|
||||
int n;
|
||||
int port;
|
||||
unsigned int maxClients;
|
||||
char name[BUFSIZ];
|
||||
|
||||
/* Ask user for server port */
|
||||
printf("\nEnter server port (default 9987): ");
|
||||
n = scanf("%d", &port);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("\nEnter server capacity (default %d): ", MAX_CLIENTS);
|
||||
n = scanf("%d", &maxClients);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ask user for server name */
|
||||
enterName(name);
|
||||
|
||||
return createVirtualServer(name, port, maxClients);
|
||||
}
|
||||
|
||||
void editVirtualServer() {
|
||||
int n;
|
||||
uint64 serverID;
|
||||
int currentSlotCount, newSlotCount;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nEnter ID of virtual server to edit: ");
|
||||
n = scanf("%llu", (unsigned long long*)&serverID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsInt(serverID, VIRTUALSERVER_MAXCLIENTS, ¤tSlotCount)) != ERROR_ok) {
|
||||
printf("Error getting the current capcity of virtual server: %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
printf("\nEnter new capacity of virtual server (currently %d): ", currentSlotCount);
|
||||
n = scanf("%d", &newSlotCount);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_setVirtualServerVariableAsInt(serverID, VIRTUALSERVER_MAXCLIENTS, newSlotCount)) != ERROR_ok) {
|
||||
printf("Error setting the new capacity: %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variable updates %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void stopVirtualServer() {
|
||||
int n;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nEnter ID of virtual server to stop: ");
|
||||
n = scanf("%llu", (unsigned long long*)&serverID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if((error = ts3server_stopVirtualServer(serverID)) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n\n", error);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *version;
|
||||
short abort = 0;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
int unknownInput = 0;
|
||||
uint64* ids;
|
||||
int i;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ServerLibFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ServerLibFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
funcs.onClientConnected = onClientConnected;
|
||||
funcs.onClientDisconnected = onClientDisconnected;
|
||||
funcs.onClientMoved = onClientMoved;
|
||||
funcs.onChannelCreated = onChannelCreated;
|
||||
funcs.onChannelEdited = onChannelEdited;
|
||||
funcs.onChannelDeleted = onChannelDeleted;
|
||||
funcs.onServerTextMessageEvent = onServerTextMessageEvent;
|
||||
funcs.onChannelTextMessageEvent = onChannelTextMessageEvent;
|
||||
funcs.onUserLoggingMessageEvent = onUserLoggingMessageEvent;
|
||||
funcs.onClientStartTalkingEvent = onClientStartTalkingEvent;
|
||||
funcs.onClientStopTalkingEvent = onClientStopTalkingEvent;
|
||||
funcs.onAccountingErrorEvent = onAccountingErrorEvent;
|
||||
funcs.onCustomPacketEncryptEvent = onCustomPacketEncryptEvent;
|
||||
funcs.onCustomPacketDecryptEvent = onCustomPacketDecryptEvent;
|
||||
#ifdef USE_VOICEDATAEVENT
|
||||
funcs.onVoiceDataEvent = onVoiceDataEvent;
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_PASSWORDS
|
||||
funcs.onClientPasswordEncrypt = onClientPasswordEncrypt;
|
||||
funcs.onCustomServerPasswordCheck = onCustomServerPasswordCheck;
|
||||
funcs.onCustomChannelPasswordCheck = onCustomChannelPasswordCheck;
|
||||
#endif
|
||||
|
||||
/* Initialize server lib with callbacks */
|
||||
if((error = ts3server_initServerLib(&funcs, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, argc, (const char* const *)argv)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Server running\n");
|
||||
|
||||
/* Query and print server lib version */
|
||||
if((error = ts3server_getServerLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Error querying server lib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Server lib version: %s\n", version);
|
||||
ts3server_freeMemory(version); /* Release dynamically allocated memory */
|
||||
|
||||
/* Create a virtual server on localhost using default port 9987 with max 10 slots */
|
||||
serverID = createVirtualServer("TS3 SDK Test Server", 9987, MAX_CLIENTS);
|
||||
printf("Created virtual server\n");
|
||||
|
||||
/* Set welcome message */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_WELCOMEMESSAGE, "Hello TeamSpeak")) != ERROR_ok) {
|
||||
printf("Error setting server welcomemessage: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Welcome message set.\n");
|
||||
|
||||
/* Set server password */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_PASSWORD, "secret")) != ERROR_ok) {
|
||||
printf("Error setting server password: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Password set.\n");
|
||||
|
||||
/* Flush above two changes to server */
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variables: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Variables flushed.\n");
|
||||
|
||||
/* Set codec quality of channel(s) */
|
||||
uint64* channelList = NULL;
|
||||
if ((error = ts3server_getChannelList(serverID, &channelList)) != ERROR_ok) {
|
||||
printf("Couldn't get channel list: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Received channel list.\n");
|
||||
|
||||
uint64* channelIDPtr = NULL;
|
||||
for (channelIDPtr = channelList; *channelIDPtr != (uint64)NULL; ++channelIDPtr) {
|
||||
if ((error = ts3server_setChannelVariableAsInt(serverID, *channelIDPtr, CHANNEL_CODEC_QUALITY, 10)) != ERROR_ok) {
|
||||
printf("Couldn't set channel codec quality: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
if ((error = ts3server_flushChannelVariable(serverID, *channelIDPtr)) != ERROR_ok) {
|
||||
if (error != ERROR_ok_no_update) {
|
||||
printf("Error flushing channel variables: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(channelList);
|
||||
|
||||
/* Simple commandline interface */
|
||||
printf("\nTeamSpeak 3 server commandline interface\n");
|
||||
showHelp();
|
||||
|
||||
while(!abort) {
|
||||
int c;
|
||||
if(unknownInput == 0) {
|
||||
printf("\nEnter Command (h for help)> ");
|
||||
}
|
||||
unknownInput = 0;
|
||||
c = getchar();
|
||||
switch(c) {
|
||||
case 'q':
|
||||
printf("\nShutting down server...\n");
|
||||
abort = 1;
|
||||
break;
|
||||
case 'h':
|
||||
showHelp();
|
||||
break;
|
||||
case 'v':
|
||||
showVirtualServers();
|
||||
break;
|
||||
case 'c':
|
||||
showChannels(serverID);
|
||||
break;
|
||||
case 'l':
|
||||
showClients(serverID);
|
||||
break;
|
||||
case 'n':
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
createDefaultChannelName(name);
|
||||
createChannel(serverID, name);
|
||||
break;
|
||||
}
|
||||
case 'N':
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
emptyInputBuffer();
|
||||
enterName(name);
|
||||
createChannel(serverID, name);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
deleteChannel(serverID);
|
||||
break;
|
||||
case 'r':
|
||||
renameChannel(serverID);
|
||||
break;
|
||||
case 'm':
|
||||
moveClient(serverID);
|
||||
break;
|
||||
case 'C':
|
||||
startVirtualServer();
|
||||
break;
|
||||
case 'E':
|
||||
editVirtualServer();
|
||||
break;
|
||||
case 'S':
|
||||
stopVirtualServer();
|
||||
break;
|
||||
default:
|
||||
unknownInput = 1;
|
||||
}
|
||||
|
||||
SLEEP(50);
|
||||
}
|
||||
|
||||
/* Stop virtual servers to make sure connected clients are notified instead of dropped */
|
||||
if((error = ts3server_getVirtualServerList(&ids)) != ERROR_ok) { /* Get array of virtual server IDs */
|
||||
printf("Error getting virtual server list: %d\n", error);
|
||||
} else {
|
||||
for(i=0; ids[i]; i++) {
|
||||
if((error = ts3server_stopVirtualServer(ids[i])) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(ids);
|
||||
}
|
||||
|
||||
/* Shutdown server lib */
|
||||
if((error = ts3server_destroyServerLib()) != ERROR_ok) {
|
||||
printf("Error destroying server lib: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/id_io.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/id_io.c"
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdio.h>
|
||||
#include "id_io.h"
|
||||
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for reading keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fgets(keyPair, BUFSIZ, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error reading keypair from file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Read keypair '%s' from file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "w");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for writing keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(keyPair, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error writing keypair to file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Wrote keypair '%s' to file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef ID_IO_H
|
||||
#define ID_IO_H
|
||||
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair);
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,778 @@
|
||||
/*
|
||||
* TeamSpeak SDK server creation params sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/serverlib_publicdefinitions.h>
|
||||
#include <teamspeak/serverlib.h>
|
||||
#include "id_io.h"
|
||||
|
||||
#define DEFAULT_VIRTUAL_SERVER_ID 1
|
||||
|
||||
/* Maximum number of clients allowed per virtual server */
|
||||
#define MAX_CLIENTS 8
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define SLEEP(x) Sleep(x)
|
||||
#else
|
||||
#define SLEEP(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
#define CHECK_ERROR(x) if((error = x) != ERROR_ok) { goto on_error; }
|
||||
|
||||
/*
|
||||
* Callback when client has connected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of connected client
|
||||
* channelID - ID of channel the client joined
|
||||
*/
|
||||
void onClientConnected(uint64 serverID, anyID clientID, uint64 channelID, unsigned int* removeClientError) {
|
||||
char* clientName;
|
||||
unsigned int error;
|
||||
|
||||
/* Query client nickname */
|
||||
if((error = ts3server_getClientVariableAsString(serverID, clientID, CLIENT_NICKNAME, &clientName)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying client nickname: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Client '%s' joined channel %llu on virtual server %llu\n", clientName, (unsigned long long) channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has disconnected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of disconnected client
|
||||
* channelID - ID of channel the client left
|
||||
*/
|
||||
void onClientDisconnected(uint64 serverID, anyID clientID, uint64 channelID) {
|
||||
printf("Client %u left channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has moved.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of moved client
|
||||
* oldChannelID - ID of old channel the client left
|
||||
* newChannelID - ID of new channel the client joined
|
||||
*/
|
||||
void onClientMoved(uint64 serverID, anyID clientID, uint64 oldChannelID, uint64 newChannelID) {
|
||||
printf("Client %u moved from channel %llu to channel %llu on virtual server %llu\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been created.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who created the channel
|
||||
* channelID - ID of the created channel
|
||||
*/
|
||||
void onChannelCreated(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu created by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been edited.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who edited the channel
|
||||
* channelID - ID of the edited channel
|
||||
*/
|
||||
void onChannelEdited(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu edited by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been deleted.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who deleted the channel
|
||||
* channelID - ID of the deleted channel
|
||||
*/
|
||||
void onChannelDeleted(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu deleted by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
void showHelp() {
|
||||
printf("\n[q] - Quit\n[h] - Show this help\n[v] - List virtual servers\n[c] - Show channels of virtual server %d\n", DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[l] - Show clients of virtual server %d\n[n] - Create new channel on virtual server %d with generated name\n[N] - Create new channel on virtual server %d with custom name\n", DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID, DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[d] - Delete channel on virtual server %d\n\n", DEFAULT_VIRTUAL_SERVER_ID);
|
||||
printf("[C] - Create new virtual server\n[E] - Edit virtual server\n[S] - Stop virtual server\n\n");
|
||||
}
|
||||
|
||||
void emptyInputBuffer() {
|
||||
int c;
|
||||
while((c = getchar()) != '\n' && c != EOF);
|
||||
}
|
||||
|
||||
void showVirtualServers() {
|
||||
uint64* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of virtual servers:\n");
|
||||
if((error = ts3server_getVirtualServerList(&ids)) != ERROR_ok) { /* Get array of virtual server IDs */
|
||||
printf("Error getting virtual server list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No virtual servers\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
int slotCount;
|
||||
char* virtualServerUniqueIdentifier;
|
||||
if((error = ts3server_getVirtualServerVariableAsString(ids[i], VIRTUALSERVER_NAME, &name)) != ERROR_ok) { /* Query server name */
|
||||
printf("Error getting virtual server nickname: %d\n", error);
|
||||
break;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsInt(ids[i], VIRTUALSERVER_MAXCLIENTS, &slotCount)) != ERROR_ok) {
|
||||
printf("Error getting virtual server slot count: %d\n", error);
|
||||
break;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsString(ids[i], VIRTUALSERVER_UNIQUE_IDENTIFIER, &virtualServerUniqueIdentifier)) != ERROR_ok) {
|
||||
printf("Error getting virtual server unique identifier: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("ID=%llu NAME=\"%s\" CAPACITY=%d Unique Identifier=\"%s\"\n", (unsigned long long)ids[i], name, slotCount, virtualServerUniqueIdentifier);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
ts3server_freeMemory(virtualServerUniqueIdentifier);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void showChannels(uint64 serverID) {
|
||||
uint64* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of channels on virtual server %llu:\n", (unsigned long long)serverID);
|
||||
if((error = ts3server_getChannelList(serverID, &ids)) != ERROR_ok) { /* Get array of channel IDs */
|
||||
printf("Error getting channel list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No channels\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
if((error = ts3server_getChannelVariableAsString(serverID, ids[i], CHANNEL_NAME, &name)) != ERROR_ok) { /* Query channel name */
|
||||
printf("Error querying channel name: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("%llu - %s\n", (unsigned long long)ids[i], name);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void showClients(uint64 serverID) {
|
||||
anyID* ids;
|
||||
int i;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nList of clients on virtual server %llu:\n", (unsigned long long)serverID);
|
||||
if((error = ts3server_getClientList(serverID, &ids)) != ERROR_ok) { /* Get array of client IDs */
|
||||
printf("Error getting client list: %d\n", error);
|
||||
return;
|
||||
}
|
||||
if(!ids[0]) {
|
||||
printf("No clients\n\n");
|
||||
ts3server_freeMemory(ids);
|
||||
return;
|
||||
}
|
||||
for(i=0; ids[i]; i++) {
|
||||
char* name;
|
||||
if((error = ts3server_getClientVariableAsString(serverID, ids[i], CLIENT_NICKNAME, &name)) != ERROR_ok) { /* Query client nickname */
|
||||
printf("Error querying client nickname: %d\n", error);
|
||||
break;
|
||||
}
|
||||
printf("%u - %s\n", ids[i], name);
|
||||
ts3server_freeMemory(name); /* Do not free memory if above function returned an error */
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
ts3server_freeMemory(ids); /* Release array */
|
||||
}
|
||||
|
||||
void createDefaultChannelName(char *name) {
|
||||
static int i = 11; /* We already have 10 channels in this example */
|
||||
sprintf(name, "Channel_%d", i++);
|
||||
}
|
||||
|
||||
void enterName(char *name) {
|
||||
char *s;
|
||||
printf("\nEnter name: ");
|
||||
fgets(name, BUFSIZ, stdin);
|
||||
s = strrchr(name, '\n');
|
||||
if(s) {
|
||||
*s = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void createChannel(uint64 serverID, const char *name) {
|
||||
/* This code demonstrates how to use the new createChannel API. */
|
||||
|
||||
unsigned int error;
|
||||
uint64 newChannelID;
|
||||
struct TS3ChannelCreationParams* ccp;
|
||||
struct TS3Variables* vars;
|
||||
|
||||
/* Create a new struct channel parameters struct. Memory is allocated in the clientlib,
|
||||
* so the struct needs to be freed using ts3server_freeMemory when done */
|
||||
error = ts3server_makeChannelCreationParams(&ccp);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to make channel creation params: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Set essential channel paramters:
|
||||
* parentID 0 -> create as top-level channel
|
||||
* channelID 0 -> server will automatically assign a new channel ID. An existing channel ID would be an error */
|
||||
error = ts3server_setChannelCreationParams(ccp, 0, 0);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to set channel creation params: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Query a struct TS3Variables, used to set additional parameters below */
|
||||
error = ts3server_getChannelCreationParamsVariables(ccp, &vars);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to get variables from channel creation params: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Use above queried struct TS3Variables to set additional channel parameters */
|
||||
|
||||
/* Set codec quality */
|
||||
error = ts3server_setVariableAsInt(vars, CHANNEL_CODEC_QUALITY, 10);
|
||||
if (error != ERROR_ok) {
|
||||
printf("Error setting channel quality: %d", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Set channel name */
|
||||
error = ts3server_setVariableAsString(vars, CHANNEL_NAME, name);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to set channel name: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Make channel permanent (important, otherwise the empty channel would be immediately deleted after creation) */
|
||||
error = ts3server_setVariableAsInt(vars, CHANNEL_FLAG_PERMANENT, 1);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to set channel name: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Set channel topic */
|
||||
error = ts3server_setVariableAsString(vars, CHANNEL_TOPIC, "My topic");
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to set channel topic: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Finally create the channel. Write the ID of the created channel into newChannelID */
|
||||
error = ts3server_createChannel(serverID, ccp, CHANNEL_CREATE_FLAG_NONE, &newChannelID);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Failed to create channel: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
printf("\nCreated channel: %llu\n\n", (unsigned long long)newChannelID);
|
||||
|
||||
leave:
|
||||
/* Cleanup struct TS3ChannelCreationParams */
|
||||
ts3server_freeMemory(ccp);
|
||||
}
|
||||
|
||||
void deleteChannel(uint64 serverID) {
|
||||
uint64 channelID;
|
||||
int n;
|
||||
unsigned int error;
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to delete: ");
|
||||
n = scanf("%llu", (unsigned long long*)&channelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Delete channel */
|
||||
if((error = ts3server_channelDelete(serverID, channelID, 0)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error deleting channel: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renameChannel(uint64 serverID) {
|
||||
uint64 channelID;
|
||||
int n;
|
||||
unsigned int error;
|
||||
char name[BUFSIZ];
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to rename: ");
|
||||
n = scanf("%llu", (unsigned long long*)&channelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Query new channel name from user */
|
||||
enterName(name);
|
||||
|
||||
/* Change channel name and flush changes */
|
||||
CHECK_ERROR(ts3server_setChannelVariableAsString(serverID, channelID, CHANNEL_NAME, name));
|
||||
CHECK_ERROR(ts3server_flushChannelVariable(serverID, channelID));
|
||||
|
||||
printf("Renamed channel %llu\n\n", (unsigned long long)channelID);
|
||||
return;
|
||||
|
||||
on_error:
|
||||
printf("Error renaming channel: %d\n\n", error);
|
||||
}
|
||||
|
||||
void moveClient(uint64 serverID) {
|
||||
anyID clientIDArray[2]; /* We only want to move one client plus terminating null end-marker */
|
||||
uint64 newChannelID; /* ID of channel to move the client into */
|
||||
unsigned int error;
|
||||
int n;
|
||||
|
||||
/* Query client ID from user */
|
||||
printf("\nEnter ID of client to move: ");
|
||||
n = scanf("%hu", &clientIDArray[0]);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
clientIDArray[1] = 0; /* Add end-marker */
|
||||
|
||||
/* Query channel ID from user */
|
||||
printf("\nEnter ID of channel to move client into: ");
|
||||
n = scanf("%llu", (unsigned long long*)&newChannelID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move client and check for error */
|
||||
if((error = ts3server_clientMove(serverID, newChannelID, clientIDArray)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error moving client: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Client %d moved to channel %llu\n", clientIDArray[0], (unsigned long long)newChannelID);
|
||||
}
|
||||
|
||||
/* Shows how to use the new server params method to create a virtual server */
|
||||
uint64 createVirtualServer2(const char* name, int port, unsigned int maxClients) {
|
||||
char buffer[BUFSIZ] = { 0 };
|
||||
char filename[BUFSIZ];
|
||||
char port_str[20];
|
||||
char* keyPair;
|
||||
unsigned int error;
|
||||
int i;
|
||||
struct TS3VirtualServerCreationParams* vscp;
|
||||
struct TS3ChannelCreationParams* ccp;
|
||||
struct TS3Variables* vars;
|
||||
uint64 serverID = 0;
|
||||
|
||||
/* Assemble filename: keypair_<port>.txt */
|
||||
strcpy(filename, "keypair_");
|
||||
sprintf(port_str, "%d", port);
|
||||
strcat(filename, port_str);
|
||||
strcat(filename, ".txt");
|
||||
|
||||
/* Try reading keyPair from file */
|
||||
if(readKeyPairFromFile(filename, buffer) == 0) {
|
||||
keyPair = buffer; /* Id read from file */
|
||||
} else {
|
||||
keyPair = ""; /* No Id saved, start virtual server with empty keyPair string */
|
||||
}
|
||||
|
||||
/* Create server creation params, write result into empty struct TS3VirtualServerCreationParams */
|
||||
error = ts3server_makeVirtualServerCreationParams(&vscp);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during makeVirtualServerCreationParams: %d\n", error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set essential connection data to server creation params:
|
||||
* port 9987, ip NULL (localhost), server keypair, max clients, channel count, virtual server ID */
|
||||
error = ts3server_setVirtualServerCreationParams(vscp, port, NULL, keyPair, maxClients, 10, 1);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during setVirtualServerCreationParams: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Query the struct TS3Variables from the server creation params, to set some additional parameters */
|
||||
error = ts3server_getVirtualServerCreationParamsVariables(vscp, &vars);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during getVirtualServerCreationParamsVariables: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Below we write some additional server parameters into the struct TS3Variables.
|
||||
* These parameters are not part of the essential parameters, which we defined earlier
|
||||
* in ts3server_setVirtualServerCreationParams */
|
||||
|
||||
/* Set virtual server name */
|
||||
error = ts3server_setVariableAsString(vars, VIRTUALSERVER_NAME, "TeamSpeak3 SDK Server");
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error setting server name: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Set virtual server password */
|
||||
error = ts3server_setVariableAsString(vars, VIRTUALSERVER_PASSWORD, "secret");
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error setting server password: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Create 10 channels, write them into the struct channel params, which we queried earlier */
|
||||
for(i = 0; i < 10; ++i) {
|
||||
/* Get the channel creation param for the channel index. This channel param structs are subobjects
|
||||
* created inside the server creation params.
|
||||
* The number of available channel params depends on the number of channels set above in
|
||||
* ts3server_setVirtualServerCreationParams (10 in this sample)
|
||||
* Write result into the reused struct TS3ChannelCreationParams. */
|
||||
error = ts3server_getVirtualServerCreationParamsChannelCreationParams(vscp, i, &ccp);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during getVirtualServerCreationParamsChannelCreationParams: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Now fill the struct channel creation params with some channel data */
|
||||
|
||||
/* Set essential data: channel parent ID and channel ID.
|
||||
* The idea here is to be able to restore a previously saved channel structure keeping the
|
||||
* same channel ID over server restarts. If we would create channels the old way, it would not
|
||||
* be possible to guarantee the channelID, as it would be assigned automatically by the server.
|
||||
* Basically this allows to recreate snapshots of the channel tree. */
|
||||
error = ts3server_setChannelCreationParams(ccp, 0, i + 1);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during setChannelCreationParams: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* As above with the server, query a TS3Variables (reused) for this channel, which we
|
||||
* can fill with some additional parameters that are not part of the essentials */
|
||||
error = ts3server_getChannelCreationParamsVariables(ccp, &vars);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during getChannelCreationParamsVariables: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Now fill the queried struct TS3Variables with additional parameters */
|
||||
|
||||
/* Make first channel default */
|
||||
if(i == 0) {
|
||||
error = ts3server_setVariableAsInt(vars, CHANNEL_FLAG_DEFAULT, 1);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error setting channel %d default: %d", (i + 1), error);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set codec quality */
|
||||
error = ts3server_setVariableAsInt(vars, CHANNEL_CODEC_QUALITY, 10);
|
||||
if (error != ERROR_ok) {
|
||||
printf("Error setting channel quality: %d", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Set channel name as "channel #" */
|
||||
sprintf(buffer, "channel %d", (i + 1));
|
||||
error = ts3server_setVariableAsString(vars, CHANNEL_NAME, buffer);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error setting channel %d name: %d", (i + 1), error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Make channel permanent */
|
||||
error = ts3server_setVariableAsInt(vars, CHANNEL_FLAG_PERMANENT, 1);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error setting channel %d permanent: %d", (i + 1), error);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally create the virtual server, using the server parameters we setup earlier.
|
||||
* In addition automatically create all channels we set in the channel parameters, which is
|
||||
* included as part of the server parameters.
|
||||
* The function writes the virtual server ID into serverID variable */
|
||||
error = ts3server_createVirtualServer2(vscp, VIRTUALSERVER_CREATE_FLAG_NONE, &serverID);
|
||||
if(error != ERROR_ok) {
|
||||
printf("Error during createVirtualServer2: %d\n", error);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
leave:
|
||||
/* Cleanup struct virtual server param. The included struct channel param will be automatically
|
||||
* freed when the virtual server param is freed, so you do not need to call freeMemory on
|
||||
* the channel params, too. */
|
||||
ts3server_freeMemory(vscp);
|
||||
|
||||
/* Finally return the virtual server ID of our just created virtual server */
|
||||
return serverID;
|
||||
}
|
||||
|
||||
uint64 startVirtualServer() {
|
||||
int n;
|
||||
int port;
|
||||
unsigned int maxClients;
|
||||
char name[BUFSIZ];
|
||||
|
||||
/* Ask user for server port */
|
||||
printf("\nEnter server port (default 9987): ");
|
||||
n = scanf("%d", &port);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("\nEnter server capacity (default %d): ", MAX_CLIENTS);
|
||||
n = scanf("%d", &maxClients);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ask user for server name */
|
||||
enterName(name);
|
||||
|
||||
return createVirtualServer2(name, port, maxClients);
|
||||
}
|
||||
|
||||
void editVirtualServer() {
|
||||
int n;
|
||||
uint64 serverID;
|
||||
int currentSlotCount, newSlotCount;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nEnter ID of virtual server to edit: ");
|
||||
n = scanf("%llu", (unsigned long long*)&serverID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_getVirtualServerVariableAsInt(serverID, VIRTUALSERVER_MAXCLIENTS, ¤tSlotCount)) != ERROR_ok) {
|
||||
printf("Error getting the current capcity of virtual server: %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
printf("\nEnter new capacity of virtual server (currently %d): ", currentSlotCount);
|
||||
n = scanf("%d", &newSlotCount);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_setVirtualServerVariableAsInt(serverID, VIRTUALSERVER_MAXCLIENTS, newSlotCount)) != ERROR_ok) {
|
||||
printf("Error setting the new capacity: %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variable updates %d\n\n", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void stopVirtualServer() {
|
||||
int n;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
|
||||
printf("\nEnter ID of virtual server to stop: ");
|
||||
n = scanf("%llu", (unsigned long long*)&serverID);
|
||||
emptyInputBuffer();
|
||||
if(n == 0) {
|
||||
printf("Invalid input. Please enter a number.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if((error = ts3server_stopVirtualServer(serverID)) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n\n", error);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *version;
|
||||
short abort = 0;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
int unknownInput = 0;
|
||||
uint64* ids;
|
||||
int i;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ServerLibFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ServerLibFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
funcs.onClientConnected = onClientConnected;
|
||||
funcs.onClientDisconnected = onClientDisconnected;
|
||||
funcs.onClientMoved = onClientMoved;
|
||||
funcs.onChannelCreated = onChannelCreated;
|
||||
funcs.onChannelEdited = onChannelEdited;
|
||||
funcs.onChannelDeleted = onChannelDeleted;
|
||||
|
||||
/* Initialize server lib with callbacks */
|
||||
if((error = ts3server_initServerLib(&funcs, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, argc, (const char* const *)argv)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Server running\n");
|
||||
|
||||
/* Query and print server lib version */
|
||||
if((error = ts3server_getServerLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Error querying server lib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Server lib version: %s\n", version);
|
||||
ts3server_freeMemory(version); /* Release dynamically allocated memory */
|
||||
|
||||
/* Create a virtual server with the new server params method */
|
||||
serverID = createVirtualServer2("TS3 SDK Test Server", 9987, MAX_CLIENTS);
|
||||
|
||||
/* Simple commandline interface */
|
||||
printf("\nTeamSpeak 3 server commandline interface\n");
|
||||
showHelp();
|
||||
|
||||
while(!abort) {
|
||||
int c;
|
||||
if(unknownInput == 0) {
|
||||
printf("\nEnter Command (h for help)> ");
|
||||
}
|
||||
unknownInput = 0;
|
||||
c = getchar();
|
||||
switch(c) {
|
||||
case 'q':
|
||||
printf("\nShutting down server...\n");
|
||||
abort = 1;
|
||||
break;
|
||||
case 'h':
|
||||
showHelp();
|
||||
break;
|
||||
case 'v':
|
||||
showVirtualServers();
|
||||
break;
|
||||
case 'c':
|
||||
showChannels(serverID);
|
||||
break;
|
||||
case 'l':
|
||||
showClients(serverID);
|
||||
break;
|
||||
case 'n':
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
createDefaultChannelName(name);
|
||||
createChannel(serverID, name);
|
||||
break;
|
||||
}
|
||||
case 'N':
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
emptyInputBuffer();
|
||||
enterName(name);
|
||||
createChannel(serverID, name);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
deleteChannel(serverID);
|
||||
break;
|
||||
case 'C':
|
||||
startVirtualServer();
|
||||
break;
|
||||
case 'E':
|
||||
editVirtualServer();
|
||||
break;
|
||||
case 'S':
|
||||
stopVirtualServer();
|
||||
break;
|
||||
default:
|
||||
unknownInput = 1;
|
||||
}
|
||||
|
||||
SLEEP(50);
|
||||
}
|
||||
|
||||
/* Stop virtual servers to make sure connected clients are notified instead of dropped */
|
||||
if((error = ts3server_getVirtualServerList(&ids)) != ERROR_ok) { /* Get array of virtual server IDs */
|
||||
printf("Error getting virtual server list: %d\n", error);
|
||||
} else {
|
||||
for(i=0; ids[i]; i++) {
|
||||
if((error = ts3server_stopVirtualServer(ids[i])) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(ids);
|
||||
}
|
||||
|
||||
/* Shutdown server lib */
|
||||
if((error = ts3server_destroyServerLib()) != ERROR_ok) {
|
||||
printf("Error destroying server lib: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/id_io.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/id_io.c"
|
||||
)
|
||||
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* TeamSpeak SDK server sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
//#define MINIMAL_EXAMPLE
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/serverlib_publicdefinitions.h>
|
||||
#include <teamspeak/serverlib.h>
|
||||
|
||||
#ifndef MINIMAL_EXAMPLE
|
||||
|
||||
char FILE_BASE[] = "sdk_files";
|
||||
|
||||
/* Small helper function to compare string endings */
|
||||
int endsWith(const char *str, const char *suffix)
|
||||
{
|
||||
size_t lenstr;
|
||||
size_t lensuffix;
|
||||
|
||||
if (!str || !suffix) return 0;
|
||||
lenstr = strlen(str);
|
||||
lensuffix = strlen(suffix);
|
||||
if (lensuffix > lenstr) return 0;
|
||||
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback triggered when a file transfer status changes
|
||||
*
|
||||
* Parameters:
|
||||
* data - The paramaters of the file transfer
|
||||
*/
|
||||
|
||||
void onFileTransferEvent(const struct FileTransferCallbackExport* data){
|
||||
|
||||
printf("onFileTransferEvent clientID: %hu, transferID: %hu, remoteTransferID: %hu, status: %u, msg: %s, remoteFileSize: %llu, bytes: %llu, isSender: %i\n",
|
||||
data->clientID, data->transferID, data->remoteTransferID, data->status, data->statusMessage, data->remotefileSize, data->bytes, data->isSender);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to upload a file
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the file upload. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the upload by returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferInitUpload(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftinitupload* params){
|
||||
|
||||
/*log to screen*/
|
||||
printf("permFileTransferInitUpload filename: %s, size: %llu, channel: %llu, overwrite: %d, resume: %d\n", params->d.fileName, params->d.fileSize, params->d.channelID, params->d.overwrite, params->d.resume);
|
||||
|
||||
/*just for fun, lets not permit uploading of .txt files*/
|
||||
if (endsWith(params->d.fileName, ".txt")){
|
||||
return ERROR_permissions;
|
||||
}
|
||||
|
||||
/*note we also have the client parameter, so we could deny based on who is uploading*/
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to upload a file
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the file download. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the download by returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferInitDownload(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftinitdownload* params){
|
||||
/*log to screen*/
|
||||
printf("permFileTransferInitDownload filename: %s, channel: %llu\n", params->d.fileName, params->d.channelID);
|
||||
|
||||
/*just for fun, lets not permit downloading of .txt files*/
|
||||
if (endsWith(params->d.fileName, ".txt")){
|
||||
return ERROR_permissions;
|
||||
}
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to get file information
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the information request. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the whole request returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferGetFileInfo(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftgetfileinfo* params){
|
||||
int idx;
|
||||
|
||||
/*log to screen*/
|
||||
printf("permFileTransferGetFileInfo \n");
|
||||
for (idx= 0; idx < params->r_size; ++idx){
|
||||
printf(" channel: %llu name:%s\n", params->r[idx].channelID, params->r[idx].fileName);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to get a directory listing
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the information request. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the whole request returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferGetFileList(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftgetfilelist* params){
|
||||
/*log to screen*/
|
||||
printf("permFileTransferGetFileList path: %s, channel: %llu\n", params->d.path, params->d.channelID);
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to delete files
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the delete request. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the whole request returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferDeleteFile(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftdeletefile* params){
|
||||
int idx;
|
||||
|
||||
/*log to screen*/
|
||||
printf("permFileTransferDeleteFile channel: %llu\n", params->d.channelID);
|
||||
for (idx= 0; idx < params->r_size; ++idx){
|
||||
printf(" name:%s\n", params->r[idx].fileName);
|
||||
|
||||
/*just for fun, lets not permit deleting of .txt files*/
|
||||
if (endsWith(params->r[idx].fileName, ".txt")){
|
||||
return ERROR_permissions;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to create a sub directory
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the request. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the whole request returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferCreateDirectory(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftcreatedir* params){
|
||||
/*log to screen*/
|
||||
printf("permFileTransferCreateDirectory dirname: %s, channel: %llu\n", params->d.dirname, params->d.channelID);
|
||||
|
||||
/*just for fun, lets not permit creation of .txt dirs*/
|
||||
if (endsWith(params->d.dirname, ".txt")){
|
||||
return ERROR_permissions;
|
||||
}
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client tries to rename a file
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is uploading the file. See public_definitions.h.
|
||||
* params - The paramaters of the rename request. See server_commands_file_transfer.h
|
||||
*
|
||||
* Note: You can deny the whole request returning ERROR_permissions
|
||||
*/
|
||||
unsigned int permFileTransferRenameFile(uint64 serverID, const struct ClientMiniExport* client, const struct ts3sc_ftrenamefile* params){
|
||||
/*log to screen*/
|
||||
if (params->m.has_toChannelID){
|
||||
printf("permFileTransferRenameFile oldname: %s, old channel: %llu new name: %s, new channel: %llu\n", params->d.oldFileName, params->d.fromChannelID, params->d.newFileName, params->d.toChannelID);
|
||||
} else {
|
||||
printf("permFileTransferRenameFile oldname: %s, old channel: %llu new name: %s\n", params->d.oldFileName, params->d.fromChannelID, params->d.newFileName);
|
||||
}
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered after most file transfer request permissions have passed. It exists to let the server change the file
|
||||
* name and/or directory of the request for special purposes.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which upload is requested.
|
||||
* invokerClientID - id of the client that is doing the request
|
||||
* original - Struct of the request params like original file name, what kind of action is taken and . See public_definitions.h.
|
||||
* result - Struct with the transformed parameters. See public_definitions.h.
|
||||
*/
|
||||
unsigned int onTransformFilePath(uint64 serverID, anyID invokerClientID, const struct TransformFilePathExport* original, struct TransformFilePathExportReturns* result){
|
||||
|
||||
const char* action;
|
||||
size_t filenamelen;
|
||||
size_t appendlen = strlen(".example");
|
||||
|
||||
switch(original->action){
|
||||
case(FT_INIT_SERVER):
|
||||
action = "FT_INIT_SERVER";
|
||||
break;
|
||||
case(FT_INIT_CHANNEL):
|
||||
action = "FT_INIT_CHANNEL";
|
||||
break;
|
||||
case(FT_UPLOAD):
|
||||
action = "FT_UPLOAD";
|
||||
break;
|
||||
case(FT_DOWNLOAD):
|
||||
action = "FT_DOWNLOAD";
|
||||
break;
|
||||
case(FT_DELETE):
|
||||
action = "FT_DELETE";
|
||||
break;
|
||||
case(FT_CREATEDIR):
|
||||
action = "FT_CREATEDIR";
|
||||
break;
|
||||
case(FT_RENAME):
|
||||
action = "FT_RENAME";
|
||||
break;
|
||||
case(FT_FILELIST):
|
||||
action = "FT_FILELIST";
|
||||
break;
|
||||
case(FT_FILEINFO):
|
||||
action = "FT_FILELIST";
|
||||
break;
|
||||
default:
|
||||
action = "unknown action";
|
||||
}
|
||||
|
||||
printf("onTransformFilePath filename: %s, action: %s\n", original->filename, action);
|
||||
|
||||
/*for FT_INIT_SERVER and FT_INIT_CHANNEL we use our own directory naming for the server/channel*/
|
||||
if (original->action == FT_INIT_SERVER){
|
||||
sprintf(result->channelPath, "%s/myvs_%llu", FILE_BASE, (unsigned long long)serverID);
|
||||
return ERROR_ok;
|
||||
}
|
||||
if (original->action == FT_INIT_CHANNEL){
|
||||
sprintf(result->channelPath, "%s/myvs_%llu/mychan_%llu", FILE_BASE, serverID, original->channel);
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/* Here we can alter the filename and file path to the data on the server. The default values are already filled in in the result variable.
|
||||
*
|
||||
* For this example we will append ".example" to .doc files
|
||||
*/
|
||||
|
||||
if (endsWith(original->filename, ".doc")){
|
||||
filenamelen = strlen(original->filename);
|
||||
if ((int)(filenamelen+appendlen) >= original->transformedFileNameMaxSize) {
|
||||
/* the filename we want to return is larger than allowed. We return an error*/
|
||||
return ERROR_parameter_invalid_size;
|
||||
}
|
||||
sprintf(result->transformedFileName, "%s.example", original->filename);
|
||||
}
|
||||
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Callback for user-defined logging.
|
||||
*
|
||||
* Parameter:
|
||||
* logMessage - Log message text
|
||||
* logLevel - Severity of log message
|
||||
* logChannel - Custom text to categorize the message channel
|
||||
* logID - Virtual server ID giving the virtual server source of the log event
|
||||
* logTime - String with the date and time the log entry occured
|
||||
* completeLogString - Verbose log message including all previous parameters for convinience
|
||||
*/
|
||||
void onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
|
||||
/* Your custom error display here... */
|
||||
/* printf("LOG: %s\n", completeLogString); */
|
||||
if(logLevel == LogLevel_CRITICAL) {
|
||||
exit(1); /* Your custom handling of critical errors */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when a license error occurs.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the license error occured. This virtual server will be automatically
|
||||
* shutdown. If the parameter is zero, all virtual servers are affected and have been shutdown.
|
||||
* errorCode - Code of the occured error. Use ts3server_getGlobalErrorMessage() to convert to a message string
|
||||
*/
|
||||
void onAccountingErrorEvent(uint64 serverID, unsigned int errorCode) {
|
||||
char* errorMessage;
|
||||
if(ts3server_getGlobalErrorMessage(errorCode, &errorMessage) == ERROR_ok) {
|
||||
printf("onAccountingErrorEvent serverID=%llu, errorCode=%u: %s\n", (unsigned long long)serverID, errorCode, errorMessage);
|
||||
ts3server_freeMemory(errorMessage);
|
||||
}
|
||||
|
||||
/* Your custom handling here. In a real application, you wouldn't stop the whole process because the TS3 server part went down.
|
||||
* The whole idea of this callback is to let you gracefully handle the TS3 server failing to start and to continue your application. */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read server key from file
|
||||
*/
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for reading keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fgets(keyPair, BUFSIZ, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error reading keypair from file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Read keypair '%s' from file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write server key to file
|
||||
*/
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "w");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for writing keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(keyPair, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error writing keypair to file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Wrote keypair '%s' to file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *version;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
char buffer[BUFSIZ] = { 0 };
|
||||
char filename[BUFSIZ];
|
||||
char port_str[20];
|
||||
char *keyPair;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ServerLibFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ServerLibFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
funcs.onUserLoggingMessageEvent = onUserLoggingMessageEvent;
|
||||
funcs.onAccountingErrorEvent = onAccountingErrorEvent;
|
||||
#ifndef MINIMAL_EXAMPLE
|
||||
funcs.permFileTransferInitUpload = permFileTransferInitUpload;
|
||||
funcs.permFileTransferInitDownload = permFileTransferInitDownload;
|
||||
funcs.permFileTransferGetFileInfo = permFileTransferGetFileInfo;
|
||||
funcs.permFileTransferGetFileList = permFileTransferGetFileList;
|
||||
funcs.permFileTransferDeleteFile = permFileTransferDeleteFile;
|
||||
funcs.permFileTransferCreateDirectory = permFileTransferCreateDirectory;
|
||||
funcs.permFileTransferRenameFile = permFileTransferRenameFile;
|
||||
funcs.onFileTransferEvent = onFileTransferEvent;
|
||||
funcs.onTransformFilePath = onTransformFilePath;
|
||||
#endif
|
||||
|
||||
/* Initialize server lib with callbacks */
|
||||
if((error = ts3server_initServerLib(&funcs, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, argc, (const char* const *)argv)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The call below is the only call that needs to be made to enable file transfers on the server.
|
||||
There are how ever a lot of callbacks that can be hooked in to (above) to change
|
||||
permissions or change file names */
|
||||
|
||||
/* Here we initialize file transfers to store everything on disk in a tree starting at "sdk_files".
|
||||
All files for virtual server with id 1 will be in "sdk_files/virtualserver_1"
|
||||
Files in channel 1 in virtual server 1 will be in "sdk_files/virtualserver_1/channel_1"
|
||||
These directories will automatically be created by the server if they do not exist. It is the
|
||||
responsibility of the application (not serverlib) to delete these directories when you are done
|
||||
with them.
|
||||
|
||||
We supply NULL to the ips param. This is equivalent to:
|
||||
char* ips[2]= {"0.0.0.0", NULL};
|
||||
If the system is ipv6 capable it is equivalent to:
|
||||
char* ips[3]= {"0.0.0.0", "::", NULL};
|
||||
|
||||
The port is free to choose. TeamSpeak defaults to 30033. Feel free to listen on an other port.
|
||||
|
||||
Finally we set no limits for the download and upload bandwidth.
|
||||
*/
|
||||
|
||||
/* Initialize server file transfers */
|
||||
if ((error=ts3server_enableFileManager(FILE_BASE, NULL, 30033, BANDWIDTH_LIMIT_UNLIMITED, BANDWIDTH_LIMIT_UNLIMITED)) != ERROR_ok){
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing filemanager: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Query and print server lib version */
|
||||
if((error = ts3server_getServerLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Error querying server lib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Server lib version: %s\n", version);
|
||||
ts3server_freeMemory(version); /* Release dynamically allocated memory */
|
||||
|
||||
/* Attempt to load keypair from file */
|
||||
/* Assemble filename: keypair_<port>.txt */
|
||||
strcpy(filename, "keypair_");
|
||||
sprintf(port_str, "%d", 9987); // Default port
|
||||
strcat(filename, port_str);
|
||||
strcat(filename, ".txt");
|
||||
|
||||
/* Try reading keyPair from file */
|
||||
if(readKeyPairFromFile(filename, buffer) == 0) {
|
||||
keyPair = buffer; /* Id read from file */
|
||||
} else {
|
||||
keyPair = ""; /* No Id saved, start virtual server with empty keyPair string */
|
||||
}
|
||||
|
||||
/* Create virtual server using default port 9987 with max 10 slots */
|
||||
|
||||
/* Create the virtual server with specified port, name, keyPair and max clients */
|
||||
printf("Create virtual server using keypair '%s'\n", keyPair);
|
||||
//listen on any address on ipv4 and ipv6 (it is also possible to enter multiple ipv4 and ipv6 addresses here)
|
||||
if((error = ts3server_createVirtualServer(9987, "0.0.0.0, ::", "TeamSpeak SDK Testserver", keyPair, 8, &serverID)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error creating virtual server: %s (%d)\n", errormsg, error);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we didn't load the keyPair before, query it from virtual server and save to file */
|
||||
if(!*buffer) {
|
||||
if((error = ts3server_getVirtualServerKeyPair(serverID, &keyPair)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying keyPair: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save keyPair to file "keypair_<port>.txt"*/
|
||||
if(writeKeyPairToFile(filename, keyPair) != 0) {
|
||||
ts3server_freeMemory(keyPair);
|
||||
return 0;
|
||||
}
|
||||
ts3server_freeMemory(keyPair);
|
||||
}
|
||||
|
||||
/* Set welcome message */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_WELCOMEMESSAGE, "Hello TeamSpeak")) != ERROR_ok) {
|
||||
printf("Error setting server welcome message: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set server password */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_PASSWORD, "secret")) != ERROR_ok) {
|
||||
printf("Error setting server password: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Flush above two changes to server */
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variables: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set codec quality of channel(s) */
|
||||
uint64* channelList = NULL;
|
||||
if ((error = ts3server_getChannelList(serverID, &channelList)) != ERROR_ok) {
|
||||
printf("Couldn't get channel list: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
uint64* channelIDPtr = NULL;
|
||||
for (channelIDPtr = channelList; *channelIDPtr != (uint64)NULL; ++channelIDPtr) {
|
||||
if ((error = ts3server_setChannelVariableAsInt(serverID, *channelIDPtr, CHANNEL_CODEC_QUALITY, 10)) != ERROR_ok) {
|
||||
printf("Couldn't set channel codec quality: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
if ((error = ts3server_flushChannelVariable(serverID, *channelIDPtr)) != ERROR_ok) {
|
||||
if (error != ERROR_ok_no_update) {
|
||||
printf("Error flushing channel variables: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(channelList);
|
||||
|
||||
/* Wait for user input */
|
||||
printf("\n--- Press Return to shutdown server and exit ---\n");
|
||||
getchar();
|
||||
|
||||
/* Stop virtual server */
|
||||
if((error = ts3server_stopVirtualServer(serverID)) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Shutdown server lib */
|
||||
if((error = ts3server_destroyServerLib()) != ERROR_ok) {
|
||||
printf("Error destroying server lib: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* TeamSpeak SDK server sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/serverlib_publicdefinitions.h>
|
||||
#include <teamspeak/serverlib.h>
|
||||
|
||||
/*
|
||||
* Callback when client has connected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of connected client
|
||||
* channelID - ID of channel the client joined
|
||||
*/
|
||||
void onClientConnected(uint64 serverID, anyID clientID, uint64 channelID, unsigned int* removeClientError) {
|
||||
printf("Client %u joined channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has disconnected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of disconnected client
|
||||
* channelID - ID of channel the client left
|
||||
*/
|
||||
void onClientDisconnected(uint64 serverID, anyID clientID, uint64 channelID) {
|
||||
printf("Client %u left channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has moved.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of moved client
|
||||
* oldChannelID - ID of old channel the client left
|
||||
* newChannelID - ID of new channel the client joined
|
||||
*/
|
||||
void onClientMoved(uint64 serverID, anyID clientID, uint64 oldChannelID, uint64 newChannelID) {
|
||||
printf("Client %u moved from channel %llu to channel %llu on virtual server %llu\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been created.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who created the channel
|
||||
* channelID - ID of the created channel
|
||||
*/
|
||||
void onChannelCreated(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu created by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been edited.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who edited the channel
|
||||
* channelID - ID of the edited channel
|
||||
*/
|
||||
void onChannelEdited(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu edited by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been deleted.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who deleted the channel
|
||||
* channelID - ID of the deleted channel
|
||||
*/
|
||||
void onChannelDeleted(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu deleted by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for user-defined logging.
|
||||
*
|
||||
* Parameter:
|
||||
* logMessage - Log message text
|
||||
* logLevel - Severity of log message
|
||||
* logChannel - Custom text to categorize the message channel
|
||||
* logID - Virtual server ID giving the virtual server source of the log event
|
||||
* logTime - String with the date and time the log entry occured
|
||||
* completeLogString - Verbose log message including all previous parameters for convinience
|
||||
*/
|
||||
void onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
|
||||
/* Your custom error display here... */
|
||||
/* printf("LOG: %s\n", completeLogString); */
|
||||
if(logLevel == LogLevel_CRITICAL) {
|
||||
exit(1); /* Your custom handling of critical errors */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client starts talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server sending the callback
|
||||
* clientID - ID of the client which started talking
|
||||
*/
|
||||
void onClientStartTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStartTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client stops talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server sending the callback
|
||||
* clientID - ID of the client which stopped talking
|
||||
*/
|
||||
void onClientStopTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStopTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when a license error occurs.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the license error occured. This virtual server will be automatically
|
||||
* shutdown. If the parameter is zero, all virtual servers are affected and have been shutdown.
|
||||
* errorCode - Code of the occured error. Use ts3server_getGlobalErrorMessage() to convert to a message string
|
||||
*/
|
||||
void onAccountingErrorEvent(uint64 serverID, unsigned int errorCode) {
|
||||
char* errorMessage;
|
||||
if(ts3server_getGlobalErrorMessage(errorCode, &errorMessage) == ERROR_ok) {
|
||||
printf("onAccountingErrorEvent serverID=%llu, errorCode=%u: %s\n", (unsigned long long)serverID, errorCode, errorMessage);
|
||||
ts3server_freeMemory(errorMessage);
|
||||
}
|
||||
|
||||
/* Your custom handling here. In a real application, you wouldn't stop the whole process because the TS3 server part went down.
|
||||
* The whole idea of this callback is to let you gracefully handle the TS3 server failing to start and to continue your application. */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read server key from file
|
||||
*/
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for reading keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fgets(keyPair, BUFSIZ, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error reading keypair from file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Read keypair '%s' from file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write server key to file
|
||||
*/
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "w");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for writing keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(keyPair, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error writing keypair to file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Wrote keypair '%s' to file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *version;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
char buffer[BUFSIZ] = { 0 };
|
||||
char filename[BUFSIZ];
|
||||
char port_str[20];
|
||||
char *keyPair;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ServerLibFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ServerLibFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
funcs.onClientConnected = onClientConnected;
|
||||
funcs.onClientDisconnected = onClientDisconnected;
|
||||
funcs.onClientMoved = onClientMoved;
|
||||
funcs.onChannelCreated = onChannelCreated;
|
||||
funcs.onChannelEdited = onChannelEdited;
|
||||
funcs.onChannelDeleted = onChannelDeleted;
|
||||
funcs.onUserLoggingMessageEvent = onUserLoggingMessageEvent;
|
||||
funcs.onClientStartTalkingEvent = onClientStartTalkingEvent;
|
||||
funcs.onClientStopTalkingEvent = onClientStopTalkingEvent;
|
||||
funcs.onAccountingErrorEvent = onAccountingErrorEvent;
|
||||
|
||||
/* Initialize server lib with callbacks */
|
||||
if((error = ts3server_initServerLib(&funcs, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, argc, (const char* const *)argv)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Query and print server lib version */
|
||||
if((error = ts3server_getServerLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Error querying server lib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Server lib version: %s\n", version);
|
||||
ts3server_freeMemory(version); /* Release dynamically allocated memory */
|
||||
|
||||
/* Attempt to load keypair from file */
|
||||
/* Assemble filename: keypair_<port>.txt */
|
||||
strcpy(filename, "keypair_");
|
||||
sprintf(port_str, "%d", 9987); // Default port
|
||||
strcat(filename, port_str);
|
||||
strcat(filename, ".txt");
|
||||
|
||||
/* Try reading keyPair from file */
|
||||
if(readKeyPairFromFile(filename, buffer) == 0) {
|
||||
keyPair = buffer; /* Id read from file */
|
||||
} else {
|
||||
keyPair = ""; /* No Id saved, start virtual server with empty keyPair string */
|
||||
}
|
||||
|
||||
/* Create virtual server using default port 9987 with max 10 slots */
|
||||
|
||||
/* Create the virtual server with specified port, name, keyPair and max clients */
|
||||
printf("Create virtual server using keypair '%s'\n", keyPair);
|
||||
//listen on any address on ipv4 and ipv6 (it is also possible to enter multiple ipv4 and ipv6 addresses here)
|
||||
if((error = ts3server_createVirtualServer(9987, "0.0.0.0, ::", "TeamSpeak SDK Testserver", keyPair, 8, &serverID)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error creating virtual server: %s (%d)\n", errormsg, error);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we didn't load the keyPair before, query it from virtual server and save to file */
|
||||
if(!*buffer) {
|
||||
if((error = ts3server_getVirtualServerKeyPair(serverID, &keyPair)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying keyPair: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save keyPair to file "keypair_<port>.txt"*/
|
||||
if(writeKeyPairToFile(filename, keyPair) != 0) {
|
||||
ts3server_freeMemory(keyPair);
|
||||
return 0;
|
||||
}
|
||||
ts3server_freeMemory(keyPair);
|
||||
}
|
||||
|
||||
/* Set welcome message */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_WELCOMEMESSAGE, "Hello TeamSpeak")) != ERROR_ok) {
|
||||
printf("Error setting server welcomemessage: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set server password */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_PASSWORD, "secret")) != ERROR_ok) {
|
||||
printf("Error setting server password: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Flush above two changes to server */
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variables: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set codec quality of channel(s) */
|
||||
uint64* channelList = NULL;
|
||||
if ((error = ts3server_getChannelList(serverID, &channelList)) != ERROR_ok) {
|
||||
printf("Couldn't get channel list: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
uint64* channelIDPtr = NULL;
|
||||
for (channelIDPtr = channelList; *channelIDPtr != (uint64)NULL; ++channelIDPtr) {
|
||||
if ((error = ts3server_setChannelVariableAsInt(serverID, *channelIDPtr, CHANNEL_CODEC_QUALITY, 10)) != ERROR_ok) {
|
||||
printf("Couldn't set channel codec quality: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
if ((error = ts3server_flushChannelVariable(serverID, *channelIDPtr)) != ERROR_ok) {
|
||||
if (error != ERROR_ok_no_update) {
|
||||
printf("Error flushing channel variables: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(channelList);
|
||||
|
||||
/* Wait for user input */
|
||||
printf("\n--- Press Return to shutdown server and exit ---\n");
|
||||
getchar();
|
||||
|
||||
/* Stop virtual server */
|
||||
if((error = ts3server_stopVirtualServer(serverID)) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Shutdown server lib */
|
||||
if((error = ts3server_destroyServerLib()) != ERROR_ok) {
|
||||
printf("Error destroying server lib: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* TeamSpeak SDK server permission sample
|
||||
*
|
||||
* Copyright (c) TeamSpeak-Systems
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <teamspeak/public_definitions.h>
|
||||
#include <teamspeak/public_errors.h>
|
||||
#include <teamspeak/serverlib_publicdefinitions.h>
|
||||
#include <teamspeak/serverlib.h>
|
||||
|
||||
/*
|
||||
* Callback when client has connected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of connected client
|
||||
* channelID - ID of channel the client joined
|
||||
*/
|
||||
void onClientConnected(uint64 serverID, anyID clientID, uint64 channelID, unsigned int* removeClientError) {
|
||||
printf("Client %u joined channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has disconnected.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of disconnected client
|
||||
* channelID - ID of channel the client left
|
||||
*/
|
||||
void onClientDisconnected(uint64 serverID, anyID clientID, uint64 channelID) {
|
||||
printf("Client %u left channel %llu on virtual server %llu\n", clientID, (unsigned long long)channelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when client has moved.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* clientID - ID of moved client
|
||||
* oldChannelID - ID of old channel the client left
|
||||
* newChannelID - ID of new channel the client joined
|
||||
*/
|
||||
void onClientMoved(uint64 serverID, anyID clientID, uint64 oldChannelID, uint64 newChannelID) {
|
||||
printf("Client %u moved from channel %llu to channel %llu on virtual server %llu\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been created.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who created the channel
|
||||
* channelID - ID of the created channel
|
||||
*/
|
||||
void onChannelCreated(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu created by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been edited.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who edited the channel
|
||||
* channelID - ID of the edited channel
|
||||
*/
|
||||
void onChannelEdited(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu edited by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback when channel has been deleted.
|
||||
*
|
||||
* Parameter:
|
||||
* serverID - Virtual server ID
|
||||
* invokerClientID - ID of the client who deleted the channel
|
||||
* channelID - ID of the deleted channel
|
||||
*/
|
||||
void onChannelDeleted(uint64 serverID, anyID invokerClientID, uint64 channelID) {
|
||||
printf("Channel %llu deleted by %u on virtual server %llu\n", (unsigned long long)channelID, invokerClientID, (unsigned long long)serverID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for user-defined logging.
|
||||
*
|
||||
* Parameter:
|
||||
* logMessage - Log message text
|
||||
* logLevel - Severity of log message
|
||||
* logChannel - Custom text to categorize the message channel
|
||||
* logID - Virtual server ID giving the virtual server source of the log event
|
||||
* logTime - String with the date and time the log entry occured
|
||||
* completeLogString - Verbose log message including all previous parameters for convinience
|
||||
*/
|
||||
void onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
|
||||
/* Your custom error display here... */
|
||||
/* printf("LOG: %s\n", completeLogString); */
|
||||
if(logLevel == LogLevel_CRITICAL) {
|
||||
exit(1); /* Your custom handling of critical errors */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client starts talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server sending the callback
|
||||
* clientID - ID of the client which started talking
|
||||
*/
|
||||
void onClientStartTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStartTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when the specified client stops talking.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server sending the callback
|
||||
* clientID - ID of the client which stopped talking
|
||||
*/
|
||||
void onClientStopTalkingEvent(uint64 serverID, anyID clientID) {
|
||||
printf("onClientStopTalkingEvent serverID=%llu, clientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered when a license error occurs.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the license error occured. This virtual server will be automatically
|
||||
* shutdown. If the parameter is zero, all virtual servers are affected and have been shutdown.
|
||||
* errorCode - Code of the occured error. Use ts3server_getGlobalErrorMessage() to convert to a message string
|
||||
*/
|
||||
void onAccountingErrorEvent(uint64 serverID, unsigned int errorCode) {
|
||||
char* errorMessage;
|
||||
if(ts3server_getGlobalErrorMessage(errorCode, &errorMessage) == ERROR_ok) {
|
||||
printf("onAccountingErrorEvent serverID=%llu, errorCode=%u: %s\n", (unsigned long long)serverID, errorCode, errorMessage);
|
||||
ts3server_freeMemory(errorMessage);
|
||||
}
|
||||
|
||||
/* Your custom handling here. In a real application, you wouldn't stop the whole process because the TS3 server part went down.
|
||||
* The whole idea of this callback is to let you gracefully handle the TS3 server failing to start and to continue your application. */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client connects.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is connecting.
|
||||
* client - Struct of client parameters like ident, nickname etc. Please view public_definitions.h.
|
||||
*
|
||||
* Note: You can deny the permission for special nicknames or identities here so the client connect is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientCanConnect(uint64 serverID, const struct ClientMiniExport* client) {
|
||||
printf("onPermClientCanConnect\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname);
|
||||
|
||||
// nickname of sdk client ist "client" so check it for a test
|
||||
if (strcmp(client->nickname, "client") == 0) {
|
||||
//return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a channel will be created.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server where the client is requesting a channel creation.
|
||||
* client - Struct of client parameters like ident, nickname etc. Please view public_definitions.h.
|
||||
* parentChannelID - Parent ID of the channel which is going to be created.
|
||||
* variables - Array of channel properties for the new channel.
|
||||
*
|
||||
* Note: You can deny the permission so creating the channel is not allowed.
|
||||
*/
|
||||
unsigned int onPermChannelCreate(uint64 serverID, const struct ClientMiniExport* client, uint64 parentChannelID, const struct VariablesExport* variables) {
|
||||
int i=0;
|
||||
|
||||
printf("onPermChannelCreate\n\tserverID=%llu\n\tparentChannelID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)parentChannelID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname);
|
||||
|
||||
// e.g. check nickname for admin and deny creation
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
|
||||
for(; i<CHANNEL_ENDMARKER; ++i) {
|
||||
struct VariablesExportItem item = variables->items[i];
|
||||
if (item.itemIsValid) {
|
||||
printf("\titem=%i itemIsValid=%i current=%s\n", i, item.itemIsValid, item.current);
|
||||
if (item.proposedIsSet) {
|
||||
printf("\titem=%i proposedIsSet=%i proposed=%s\n", i, item.proposedIsSet, item.proposed);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a channel description will be sent.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting the channel description.
|
||||
* client - Struct of client parameters like ident, nickname etc. Please view public_definitions.h.
|
||||
*
|
||||
* Note: You can deny the permission so getting the channel descritpion is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientCanGetChannelDescription(uint64 serverID, const struct ClientMiniExport* client) {
|
||||
printf("onPermClientCanGetChannelDescription\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a client update
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting an udpate.
|
||||
* clientID - ID of the client which triggered an update.
|
||||
* variables - Array of client properties.
|
||||
*
|
||||
* Note: You can deny the permission so updating a client variable is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientUpdate(uint64 serverID, anyID clientID, const struct VariablesExport* variables) {
|
||||
int i=0;
|
||||
|
||||
printf("onPermClientUpdate\n\tserverID=%llu\n\tclientID=%u\n", (unsigned long long)serverID, clientID);
|
||||
|
||||
for(; i<CLIENT_ENDMARKER; ++i) {
|
||||
struct VariablesExportItem item = variables->items[i];
|
||||
if (item.itemIsValid) {
|
||||
printf("\titem=%i itemIsValid=%i current=%s\n", i, item.itemIsValid, item.current);
|
||||
if (item.proposedIsSet) {
|
||||
printf("\titem=%i proposedIsSet=%i proposed=%s\n", i, item.proposedIsSet, item.proposed);
|
||||
// e.g. check nickname for Admin and deny
|
||||
if (i == CLIENT_NICKNAME && strcmp(item.proposed, "Admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before one or more clients will be kicked from channel.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting the kick.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is causing the kick. Please view public_definitions.h.
|
||||
* toKickCount - The number of clients to be kicked.
|
||||
* toKickClients - Array of structs which clients are going to be kicked.
|
||||
* reasonText - Optional reason text why the kick was initiated.
|
||||
*
|
||||
* Note: You can deny the permission so kicking is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientKickFromChannel(uint64 serverID, const struct ClientMiniExport* client, int toKickCount, const struct ClientMiniExport* toKickClients, const char* reasonText) {
|
||||
printf("onPermClientKickFromChannel\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\ttoKickCount=%i\n"
|
||||
"\ttoKickClientsChannel=%llu\n\ttoKickClientsClientID=%u\n\ttoKickClientsIdent=%s\n\ttoKickClientsNickname=%s\n"
|
||||
"\treasonText=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname, toKickCount, (unsigned long long)toKickClients->channel, toKickClients->ID, toKickClients->ident, toKickClients->nickname, reasonText);
|
||||
// e.g. check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before one or more clients will be kicked from server.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting the kick.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is causing the kick. Please view public_definitions.h.
|
||||
* toKickCount - The number of clients to be kicked.
|
||||
* toKickClients - Array of structs which clients are going to be kicked.
|
||||
* reasonText - Optional reason text why the kick was initiated.
|
||||
*
|
||||
* Note: You can deny the permission so kicking is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientKickFromServer(uint64 serverID, const struct ClientMiniExport* client, int toKickCount, const struct ClientMiniExport* toKickClients, const char* reasonText) {
|
||||
printf("onPermClientKickFromServer\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\ttoKickCount=%i\n"
|
||||
"\ttoKickClientsChannel=%llu\n\ttoKickClientsClientID=%u\n\ttoKickClientsIdent=%s\n\ttoKickClientsNickname=%s\n"
|
||||
"\treasonText=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname, toKickCount, (unsigned long long)toKickClients->channel, toKickClients->ID, toKickClients->ident, toKickClients->nickname, reasonText);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before one or more clients will be moved.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting the client move.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is causing the move. Please view public_definitions.h.
|
||||
* toMoveCount - The number of clients to be moved.
|
||||
* toMoveClients - Array of structs which clients are going to be moved.
|
||||
* newChannel - ID of the new channel.
|
||||
* reasonText - The reason why the move was initiated.
|
||||
*
|
||||
* Note: You can deny the permission so moving is not allowed.
|
||||
*/
|
||||
unsigned int onPermClientMove(uint64 serverID, const struct ClientMiniExport* client, int toMoveCount, const struct ClientMiniExport* toMoveClients, uint64 newChannel, const char* reasonText) {
|
||||
printf("onPermClientMove\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\ttoMoveCount=%i\n"
|
||||
"\ttoMoveClientsChannel=%llu\n\ttoMoveClientsClientID=%u\n\ttoMoveClientsIdent=%s\n\ttoMoveClientsNickname=%s\n"
|
||||
"\tnewChannel=%llu\n\treasonText=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname, toMoveCount, (unsigned long long)toMoveClients->channel, toMoveClients->ID, toMoveClients->ident, toMoveClients->nickname,(unsigned long long)newChannel, reasonText);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before a channel will be moved.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting the channel move.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is causing the move. Please view public_definitions.h.
|
||||
* channelID - ID of the current channel.
|
||||
* newParentChannelID - ID to which parent channel the current channel is going to be moved.
|
||||
*
|
||||
* Note: You can deny the permission so moving is not allowed.
|
||||
*/
|
||||
unsigned int onPermChannelMove(uint64 serverID, const struct ClientMiniExport* client, uint64 channelID, uint64 newParentChannelID) {
|
||||
printf("onPermChannelMove\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\tchannelID=%llu\n\tnewParentChannelID=%llu\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname,(unsigned long long)channelID, (unsigned long long)newParentChannelID);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before message will be sent.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is sending the textmessage. Please view public_definitions.h.
|
||||
* targetMode - The text message target mode if it is a server, channel or client message.
|
||||
* targetClientOrChannel - ID of the target client or the target channel.
|
||||
* textMessage - The sent text message.
|
||||
*
|
||||
* Note: You can deny the permission so sending a text message is not allowed.
|
||||
*/
|
||||
unsigned int onPermSendTextMessage(uint64 serverID, const struct ClientMiniExport* client, anyID targetMode, uint64 targetClientOrChannel, const char* textMessage) {
|
||||
printf("onPermSendTextMessage\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\ttargetMode=%u\n\ttargetClientOrChannel=%llu\n"
|
||||
"\ttextMessage=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname, targetMode, (unsigned long long)targetClientOrChannel, textMessage);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before server connection info will be sent.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is requesting the connection info. Please view public_definitions.h.
|
||||
*
|
||||
* Note: You can deny the permission so requesting the connection info is not allowed.
|
||||
*/
|
||||
unsigned int onPermServerRequestConnectionInfo(uint64 serverID, const struct ClientMiniExport* client) {
|
||||
printf("onPermServerRequestConnectionInfo\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before client connection info will be sent.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is requesting the connection info. Please view public_definitions.h.
|
||||
* mayViewIpPort - Change it to 0 for decline and 1 to allow viewing ip and port. Default is allow. Also, this param is ignored if client==targetClient
|
||||
* targetClient - Struct of target client parameters like ident, nickname etc. Please view public_definitions.h.
|
||||
*
|
||||
* Note: You can deny the permission so requesting the connection info is not allowed.
|
||||
*/
|
||||
unsigned int onPermSendConnectionInfo(uint64 serverID, const struct ClientMiniExport* client, int* mayViewIpPort, const struct ClientMiniExport* targetClient) {
|
||||
*mayViewIpPort = 1;
|
||||
printf("onPermSendConnectionInfo\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\ttargetClientChannel=%llu\n\ttargetClientClientID=%u\n\ttargetClientIdent=%s\n\ttargetClientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname, (unsigned long long)targetClient->channel, targetClient->ID, targetClient->ident, targetClient->nickname);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before channel will be edited.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is requesting the channel edit. Please view public_definitions.h.
|
||||
* channelID - ID of the channel which is going to be edited.
|
||||
* variables - Array of channel properties.
|
||||
*
|
||||
* Note: You can deny the permission so editing the channel is not allowed.
|
||||
*/
|
||||
unsigned int onPermChannelEdit(uint64 serverID, const struct ClientMiniExport* client, uint64 channelID, const struct VariablesExport* variables) {
|
||||
int i=0;
|
||||
printf("onPermChannelEdit\n\tserverID=%llu\n\tchannelID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n",
|
||||
(unsigned long long)serverID, (unsigned long long)channelID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname);
|
||||
|
||||
for(; i<CHANNEL_ENDMARKER; ++i) {
|
||||
struct VariablesExportItem item = variables->items[i];
|
||||
if (item.itemIsValid) {
|
||||
printf("\titem=%i itemIsValid=%i current=%s\n", i, item.itemIsValid, item.current);
|
||||
if (item.proposedIsSet) {
|
||||
printf("\titem=%i proposedIsSet=%i proposed=%s\n", i, item.proposedIsSet, item.proposed);
|
||||
// check channel name for admin and deny
|
||||
if (i == CHANNEL_NAME && strcmp(item.proposed, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before channel will be deleted.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is requesting the channel edit. Please view public_definitions.h.
|
||||
* channelID - ID of the channel which is going to be edited.
|
||||
*
|
||||
* Note: You can deny the permission so deleting the channel is not allowed.
|
||||
*/
|
||||
unsigned int onPermChannelDelete(uint64 serverID, const struct ClientMiniExport* client, uint64 channelID) {
|
||||
printf("onPermChannelDelete\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\tchannelID=%llu\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname,(unsigned long long)channelID);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback triggered before channel will be subscribed.
|
||||
*
|
||||
* Parameters:
|
||||
* serverID - ID of the virtual server on which the client is requesting to sent a textmessage.
|
||||
* client - Struct of client parameters like ident, nickname etc. who is requesting the channel edit. Please view public_definitions.h.
|
||||
* channelID - ID of the channel which is going to be edited.
|
||||
*
|
||||
* Note: You can deny the permission so subscribing the channel is not allowed.
|
||||
*/
|
||||
unsigned int onPermChannelSubscribe(uint64 serverID, const struct ClientMiniExport* client, uint64 channelID) {
|
||||
printf("onPermChannelSubscribe\n\tserverID=%llu\n"
|
||||
"\tclientChannel=%llu\n\tclientClientID=%u\n\tclientIdent=%s\n\tclientNickname=%s\n"
|
||||
"\tchannelID=%llu\n",
|
||||
(unsigned long long)serverID, (unsigned long long)client->channel, client->ID, client->ident, client->nickname,(unsigned long long)channelID);
|
||||
// check nickname for admin and deny
|
||||
if (strcmp(client->nickname, "admin") == 0) {
|
||||
return ERROR_permissions;
|
||||
}
|
||||
return ERROR_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read server key from file
|
||||
*/
|
||||
int readKeyPairFromFile(const char *fileName, char *keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "r");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for reading keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fgets(keyPair, BUFSIZ, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error reading keypair from file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Read keypair '%s' from file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write server key to file
|
||||
*/
|
||||
int writeKeyPairToFile(const char *fileName, const char* keyPair) {
|
||||
FILE *file;
|
||||
|
||||
file = fopen(fileName, "w");
|
||||
if(file == NULL) {
|
||||
printf("Could not open file '%s' for writing keypair\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(keyPair, file);
|
||||
if(ferror(file) != 0) {
|
||||
fclose (file);
|
||||
printf("Error writing keypair to file '%s'.\n", fileName);
|
||||
return -1;
|
||||
}
|
||||
fclose (file);
|
||||
|
||||
printf("Wrote keypair '%s' to file '%s'.\n", keyPair, fileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *version;
|
||||
uint64 serverID;
|
||||
unsigned int error;
|
||||
char buffer[BUFSIZ] = { 0 };
|
||||
char filename[BUFSIZ];
|
||||
char port_str[20];
|
||||
char *keyPair;
|
||||
|
||||
/* Create struct for callback function pointers */
|
||||
struct ServerLibFunctions funcs;
|
||||
|
||||
/* Initialize all callbacks with NULL */
|
||||
memset(&funcs, 0, sizeof(struct ServerLibFunctions));
|
||||
|
||||
/* Now assign the used callback function pointers */
|
||||
//funcs.onClientConnected = onClientConnected;
|
||||
//funcs.onClientDisconnected = onClientDisconnected;
|
||||
//funcs.onClientMoved = onClientMoved;
|
||||
//funcs.onChannelCreated = onChannelCreated;
|
||||
//funcs.onChannelEdited = onChannelEdited;
|
||||
//funcs.onChannelDeleted = onChannelDeleted;
|
||||
//funcs.onUserLoggingMessageEvent = onUserLoggingMessageEvent;
|
||||
//funcs.onClientStartTalkingEvent = onClientStartTalkingEvent;
|
||||
//funcs.onClientStopTalkingEvent = onClientStopTalkingEvent;
|
||||
//funcs.onAccountingErrorEvent = onAccountingErrorEvent;
|
||||
|
||||
funcs.permClientCanConnect = onPermClientCanConnect;
|
||||
funcs.permClientCanGetChannelDescription = onPermClientCanGetChannelDescription;
|
||||
funcs.permClientUpdate = onPermClientUpdate;
|
||||
funcs.permClientKickFromChannel = onPermClientKickFromChannel;
|
||||
funcs.permClientKickFromServer = onPermClientKickFromServer;
|
||||
funcs.permClientMove = onPermClientMove;
|
||||
funcs.permChannelMove = onPermChannelMove;
|
||||
funcs.permSendTextMessage = onPermSendTextMessage;
|
||||
funcs.permSendConnectionInfo = onPermSendConnectionInfo;
|
||||
funcs.permServerRequestConnectionInfo = onPermServerRequestConnectionInfo;
|
||||
funcs.permChannelCreate = onPermChannelCreate;
|
||||
funcs.permChannelEdit = onPermChannelEdit;
|
||||
funcs.permChannelDelete = onPermChannelDelete;
|
||||
funcs.permChannelSubscribe = onPermChannelSubscribe;
|
||||
|
||||
/* Initialize server lib with callbacks */
|
||||
if((error = ts3server_initServerLib(&funcs, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, argc, (const char* const *)argv)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error initialzing serverlib: %s\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Query and print server lib version */
|
||||
if((error = ts3server_getServerLibVersion(&version)) != ERROR_ok) {
|
||||
printf("Error querying server lib version: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
printf("Server lib version: %s\n", version);
|
||||
ts3server_freeMemory(version); /* Release dynamically allocated memory */
|
||||
|
||||
/* Attempt to load keypair from file */
|
||||
/* Assemble filename: keypair_<port>.txt */
|
||||
strcpy(filename, "keypair_");
|
||||
sprintf(port_str, "%d", 9987); // Default port
|
||||
strcat(filename, port_str);
|
||||
strcat(filename, ".txt");
|
||||
|
||||
/* Try reading keyPair from file */
|
||||
if(readKeyPairFromFile(filename, buffer) == 0) {
|
||||
keyPair = buffer; /* Id read from file */
|
||||
} else {
|
||||
keyPair = ""; /* No Id saved, start virtual server with empty keyPair string */
|
||||
}
|
||||
|
||||
/* Create virtual server using default port 9987 with max 10 slots */
|
||||
|
||||
/* Create the virtual server with specified port, name, keyPair and max clients */
|
||||
printf("Create virtual server using keypair '%s'\n", keyPair);
|
||||
//listen on any address on ipv4 and ipv6 (it is also possible to enter multiple ipv4 and ipv6 addresses here)
|
||||
if((error = ts3server_createVirtualServer(9987, "0.0.0.0, ::", "TeamSpeak SDK Testserver", keyPair, 8, &serverID)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error creating virtual server: %s (%d)\n", errormsg, error);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we didn't load the keyPair before, query it from virtual server and save to file */
|
||||
if(!*buffer) {
|
||||
if((error = ts3server_getVirtualServerKeyPair(serverID, &keyPair)) != ERROR_ok) {
|
||||
char* errormsg;
|
||||
if(ts3server_getGlobalErrorMessage(error, &errormsg) == ERROR_ok) {
|
||||
printf("Error querying keyPair: %s\n\n", errormsg);
|
||||
ts3server_freeMemory(errormsg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save keyPair to file "keypair_<port>.txt"*/
|
||||
if(writeKeyPairToFile(filename, keyPair) != 0) {
|
||||
ts3server_freeMemory(keyPair);
|
||||
return 0;
|
||||
}
|
||||
ts3server_freeMemory(keyPair);
|
||||
}
|
||||
|
||||
/* Set welcome message */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_WELCOMEMESSAGE, "Hello TeamSpeak")) != ERROR_ok) {
|
||||
printf("Error setting server welcomemessage: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set server password */
|
||||
if((error = ts3server_setVirtualServerVariableAsString(serverID, VIRTUALSERVER_PASSWORD, "secret")) != ERROR_ok) {
|
||||
printf("Error setting server password: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Flush above two changes to server */
|
||||
if((error = ts3server_flushVirtualServerVariable(serverID)) != ERROR_ok) {
|
||||
printf("Error flushing server variables: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set codec quality of channel(s) */
|
||||
uint64* channelList = NULL;
|
||||
if ((error = ts3server_getChannelList(serverID, &channelList)) != ERROR_ok) {
|
||||
printf("Couldn't get channel list: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
uint64* channelIDPtr = NULL;
|
||||
for (channelIDPtr = channelList; *channelIDPtr != (uint64)NULL; ++channelIDPtr) {
|
||||
if ((error = ts3server_setChannelVariableAsInt(serverID, *channelIDPtr, CHANNEL_CODEC_QUALITY, 10)) != ERROR_ok) {
|
||||
printf("Couldn't set channel codec quality: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
if ((error = ts3server_flushChannelVariable(serverID, *channelIDPtr)) != ERROR_ok) {
|
||||
if (error != ERROR_ok_no_update) {
|
||||
printf("Error flushing channel variables: %d\n", error);
|
||||
ts3server_freeMemory(channelList);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ts3server_freeMemory(channelList);
|
||||
|
||||
/* Wait for user input */
|
||||
printf("\n--- Press Return to shutdown server and exit ---\n");
|
||||
getchar();
|
||||
|
||||
/* Stop virtual server */
|
||||
if((error = ts3server_stopVirtualServer(serverID)) != ERROR_ok) {
|
||||
printf("Error stopping virtual server: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Shutdown server lib */
|
||||
if((error = ts3server_destroyServerLib()) != ERROR_ok) {
|
||||
printf("Error destroying server lib: %d\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
message("generating TeamSpeak SDK sample ${TS_SDK_SAMPLE}")
|
||||
|
||||
set (TS_SAMPLE_SRC
|
||||
"${CMAKE_CURRENT_LIST_DIR}/main.c"
|
||||
)
|
||||
Reference in New Issue
Block a user