Initial video commit
This commit is contained in:
parent
6cd481e824
commit
a37ba81a4f
|
@ -0,0 +1,66 @@
|
||||||
|
# Generated from CLion C/C++ Code Style settings
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: 0
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignOperands: true
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Always
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
ColumnLimit: 0
|
||||||
|
CompactNamespaces: false
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 4
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 2
|
||||||
|
NamespaceIndentation: All
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 0
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
|
@ -8,3 +8,6 @@
|
||||||
[submodule "music"]
|
[submodule "music"]
|
||||||
path = music
|
path = music
|
||||||
url = https://github.com/TeaSpeak/TeaMusic-Providers.git
|
url = https://github.com/TeaSpeak/TeaMusic-Providers.git
|
||||||
|
[submodule "rtclib"]
|
||||||
|
path = rtclib
|
||||||
|
url = https://git.did.science/TeaSpeak/Server/rtc.git
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3133288d211f5b85db59a390ae50e42e37cf0f18
|
Subproject commit d09428739c651d897ae5b00b394139891050428a
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"name": "TeaSpeak",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "^14.11.2",
|
|
||||||
"yaml": "^1.10.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 69a7427ed59bd2177671eabf0df999b98b008ae0
|
|
@ -159,6 +159,8 @@ set(SERVER_SOURCE_FILES
|
||||||
|
|
||||||
src/server/voice/UDPVoiceServer.cpp
|
src/server/voice/UDPVoiceServer.cpp
|
||||||
src/server/voice/DatagramPacket.cpp
|
src/server/voice/DatagramPacket.cpp
|
||||||
|
|
||||||
|
src/rtc/lib.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (COMPILE_WEB_CLIENT)
|
if (COMPILE_WEB_CLIENT)
|
||||||
|
@ -169,10 +171,7 @@ if (COMPILE_WEB_CLIENT)
|
||||||
|
|
||||||
src/server/WebServer.cpp
|
src/server/WebServer.cpp
|
||||||
src/client/web/WebClient.cpp
|
src/client/web/WebClient.cpp
|
||||||
# src/server/web/WebRTCServer.cpp
|
|
||||||
src/client/web/WSWebClient.cpp
|
src/client/web/WSWebClient.cpp
|
||||||
src/client/web/SampleHandler.cpp
|
|
||||||
src/client/web/VoiceBridge.cpp
|
|
||||||
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h)
|
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
@ -256,8 +255,8 @@ target_link_libraries(PermMapHelper
|
||||||
|
|
||||||
|
|
||||||
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
|
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||||
SET(CPACK_PACKAGE_VERSION_MINOR "4")
|
SET(CPACK_PACKAGE_VERSION_MINOR "5")
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH "22")
|
SET(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||||
if (BUILD_TYPE_NAME EQUAL OFF)
|
if (BUILD_TYPE_NAME EQUAL OFF)
|
||||||
SET(CPACK_PACKAGE_VERSION_DATA "beta")
|
SET(CPACK_PACKAGE_VERSION_DATA "beta")
|
||||||
elseif (BUILD_TYPE_NAME STREQUAL "")
|
elseif (BUILD_TYPE_NAME STREQUAL "")
|
||||||
|
@ -290,7 +289,7 @@ target_link_libraries(TeaSpeakServer
|
||||||
|
|
||||||
#Require a so
|
#Require a so
|
||||||
sqlite3
|
sqlite3
|
||||||
DataPipes::rtc::shared
|
DataPipes::core::shared
|
||||||
|
|
||||||
breakpad::static
|
breakpad::static
|
||||||
protobuf::libprotobuf
|
protobuf::libprotobuf
|
||||||
|
@ -304,15 +303,10 @@ target_link_libraries(TeaSpeakServer
|
||||||
zstd::libzstd_static
|
zstd::libzstd_static
|
||||||
)
|
)
|
||||||
|
|
||||||
if (COMPILE_WEB_CLIENT)
|
target_link_libraries(TeaSpeakServer
|
||||||
file(GLOB GLIB20_ARCHS ${glib20_DIR}/lib/*)
|
${CMAKE_SOURCE_DIR}/rtclib/target/debug/libteaspeak_rtc.so
|
||||||
list(LENGTH GLIB20_ARCHS GLIB20_ARCHS_LENGTH)
|
${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 # FIXME: Remove this!
|
||||||
if (NOT ${GLIB20_ARCHS_LENGTH} EQUAL 1)
|
)
|
||||||
message(FATAL_ERROR "Missing arch specific folder for glib2.0 in ${glib20_DIR}. Found ${GLIB20_ARCHS_LENGTH} directories, expected 1.")
|
|
||||||
endif ()
|
|
||||||
list(GET GLIB20_ARCHS 0 GLIB20_ARCH_DIR)
|
|
||||||
target_link_libraries(TeaSpeakServer ${GLIB20_ARCH_DIR}/libffi.so.7 ${nice_DIR}/lib/libnice.so.10)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# include_directories(${LIBRARY_PATH}/boringssl/include/)
|
# include_directories(${LIBRARY_PATH}/boringssl/include/)
|
||||||
target_link_libraries(TeaSpeakServer
|
target_link_libraries(TeaSpeakServer
|
||||||
|
|
|
@ -44,9 +44,10 @@ extern void testTomMath();
|
||||||
#define DB_NAME "TeaData.sqlite"
|
#define DB_NAME "TeaData.sqlite"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
#include <src/terminal/PipedTerminal.h>
|
|
||||||
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
|
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
|
||||||
|
#include <codecvt>
|
||||||
|
#include <src/rtc/lib.h>
|
||||||
|
#include <src/terminal/PipedTerminal.h>
|
||||||
|
|
||||||
class CLIParser {
|
class CLIParser {
|
||||||
public:
|
public:
|
||||||
|
@ -189,7 +190,6 @@ int main(int argc, char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CLIParser arguments(argc, argv);
|
CLIParser arguments(argc, argv);
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
OpenSSL_add_ssl_algorithms();
|
OpenSSL_add_ssl_algorithms();
|
||||||
|
@ -311,9 +311,12 @@ int main(int argc, char** argv) {
|
||||||
//http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
|
//http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
|
||||||
std::string descriptors = "LTGE";
|
std::string descriptors = "LTGE";
|
||||||
bool crypt_init = false;
|
bool crypt_init = false;
|
||||||
for(const auto& c : descriptors)
|
for(const auto& c : descriptors) {
|
||||||
if((crypt_init |= crypt_mp_init(&c)))
|
if((crypt_init |= crypt_mp_init(&c))) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!crypt_init) {
|
if(!crypt_init) {
|
||||||
logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
|
logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -363,6 +366,18 @@ int main(int argc, char** argv) {
|
||||||
};
|
};
|
||||||
|
|
||||||
logger::updateLogLevels();
|
logger::updateLogLevels();
|
||||||
|
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
|
||||||
|
|
||||||
|
{
|
||||||
|
debugMessage(LOG_GENERAL, "Initializing RTP library version {}", ts::rtc::version());
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
if(!ts::rtc::initialize(error)) {
|
||||||
|
logCritical(LOG_GENERAL, "Failed to initialize RTC library: {}", error);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
|
if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
|
||||||
logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
|
logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
|
||||||
logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string());
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string());
|
||||||
|
@ -409,7 +424,6 @@ int main(int argc, char** argv) {
|
||||||
rlimit_updates:;
|
rlimit_updates:;
|
||||||
}
|
}
|
||||||
|
|
||||||
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
|
|
||||||
logMessage(LOG_GENERAL, "Starting music providers");
|
logMessage(LOG_GENERAL, "Starting music providers");
|
||||||
|
|
||||||
if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
|
if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
|
||||||
|
|
|
@ -64,13 +64,14 @@ bool config::server::authentication::name;
|
||||||
|
|
||||||
bool config::server::clients::teamspeak;
|
bool config::server::clients::teamspeak;
|
||||||
std::string config::server::clients::extra_welcome_message_teamspeak;
|
std::string config::server::clients::extra_welcome_message_teamspeak;
|
||||||
|
std::string config::server::clients::teamspeak_not_allowed_message;
|
||||||
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teamspeak;
|
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teamspeak;
|
||||||
|
|
||||||
bool config::server::clients::teaweb;
|
bool config::server::clients::teaweb;
|
||||||
std::string config::server::clients::extra_welcome_message_teaweb;
|
std::string config::server::clients::extra_welcome_message_teaweb;
|
||||||
|
std::string config::server::clients::teaweb_not_allowed_message;
|
||||||
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaweb;
|
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaweb;
|
||||||
|
|
||||||
bool config::server::clients::teaspeak;
|
|
||||||
std::string config::server::clients::extra_welcome_message_teaspeak;
|
std::string config::server::clients::extra_welcome_message_teaspeak;
|
||||||
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaspeak;
|
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaspeak;
|
||||||
|
|
||||||
|
@ -1365,6 +1366,12 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
|
||||||
ADD_DESCRIPTION("Allow/disallow the TeamSpeak 3 client to join the server.");
|
ADD_DESCRIPTION("Allow/disallow the TeamSpeak 3 client to join the server.");
|
||||||
ADD_NOTE_RELOADABLE();
|
ADD_NOTE_RELOADABLE();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
CREATE_BINDING("not_allowed_message", FLAG_RELOADABLE);
|
||||||
|
BIND_STRING(config::server::clients::teamspeak_not_allowed_message, "");
|
||||||
|
ADD_DESCRIPTION("Chaneg the message, displayed when denying the access to the server");
|
||||||
|
ADD_NOTE_RELOADABLE();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
CREATE_BINDING("teamspeak_message", FLAG_RELOADABLE);
|
CREATE_BINDING("teamspeak_message", FLAG_RELOADABLE);
|
||||||
BIND_STRING(config::server::clients::extra_welcome_message_teamspeak, "");
|
BIND_STRING(config::server::clients::extra_welcome_message_teamspeak, "");
|
||||||
|
@ -1390,7 +1397,6 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
|
||||||
ADD_NOTE_RELOADABLE();
|
ADD_NOTE_RELOADABLE();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
config::server::clients::teaspeak = true;
|
|
||||||
{
|
{
|
||||||
CREATE_BINDING("teaspeak_message", FLAG_RELOADABLE);
|
CREATE_BINDING("teaspeak_message", FLAG_RELOADABLE);
|
||||||
BIND_STRING(config::server::clients::extra_welcome_message_teaspeak, "");
|
BIND_STRING(config::server::clients::extra_welcome_message_teaspeak, "");
|
||||||
|
@ -1415,6 +1421,12 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
|
||||||
ADD_DESCRIPTION("Allow/disallow the TeaSpeak - Web client to join the server.");
|
ADD_DESCRIPTION("Allow/disallow the TeaSpeak - Web client to join the server.");
|
||||||
ADD_NOTE_RELOADABLE();
|
ADD_NOTE_RELOADABLE();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
CREATE_BINDING("not_allowed_message", FLAG_RELOADABLE);
|
||||||
|
BIND_STRING(config::server::clients::teaweb_not_allowed_message, "");
|
||||||
|
ADD_DESCRIPTION("Chaneg the message, displayed when denying the access to the server");
|
||||||
|
ADD_NOTE_RELOADABLE();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
CREATE_BINDING("teaweb_message", FLAG_RELOADABLE);
|
CREATE_BINDING("teaweb_message", FLAG_RELOADABLE);
|
||||||
BIND_STRING(config::server::clients::extra_welcome_message_teaweb, "");
|
BIND_STRING(config::server::clients::extra_welcome_message_teaweb, "");
|
||||||
|
|
|
@ -103,14 +103,15 @@ namespace ts::config {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool teamspeak;
|
extern bool teamspeak;
|
||||||
|
extern std::string teamspeak_not_allowed_message;
|
||||||
extern std::string extra_welcome_message_teamspeak;
|
extern std::string extra_welcome_message_teamspeak;
|
||||||
extern WelcomeMessageType extra_welcome_message_type_teamspeak;
|
extern WelcomeMessageType extra_welcome_message_type_teamspeak;
|
||||||
|
|
||||||
extern bool teaspeak;
|
|
||||||
extern std::string extra_welcome_message_teaspeak;
|
extern std::string extra_welcome_message_teaspeak;
|
||||||
extern WelcomeMessageType extra_welcome_message_type_teaspeak;
|
extern WelcomeMessageType extra_welcome_message_type_teaspeak;
|
||||||
|
|
||||||
extern bool teaweb;
|
extern bool teaweb;
|
||||||
|
extern std::string teaweb_not_allowed_message;
|
||||||
extern std::string extra_welcome_message_teaweb;
|
extern std::string extra_welcome_message_teaweb;
|
||||||
extern WelcomeMessageType extra_welcome_message_type_teaweb;
|
extern WelcomeMessageType extra_welcome_message_type_teaweb;
|
||||||
|
|
||||||
|
|
|
@ -248,13 +248,6 @@ bool VirtualServer::could_default_create_channel() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
for (auto &cl : this->server->getClients())
|
|
||||||
if (cl->isClientVisible(client) || client == cl)
|
|
||||||
cl->notifyClientLeftViewKicked(client, client->currentChannel, nullptr, cmd["reasonmsg"].as<std::string>(), this);
|
|
||||||
*/
|
|
||||||
|
|
||||||
void VirtualServer::notify_client_ban(const shared_ptr<ConnectedClient> &target, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &reason, size_t time) {
|
void VirtualServer::notify_client_ban(const shared_ptr<ConnectedClient> &target, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &reason, size_t time) {
|
||||||
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
|
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
|
||||||
lock_guard command_lock(target->command_lock);
|
lock_guard command_lock(target->command_lock);
|
||||||
|
@ -307,6 +300,9 @@ void VirtualServer::notify_client_kick(
|
||||||
|
|
||||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(target->currentChannel);
|
auto s_channel = dynamic_pointer_cast<ServerChannel>(target->currentChannel);
|
||||||
s_channel->unregister_client(target);
|
s_channel->unregister_client(target);
|
||||||
|
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
|
||||||
|
this->rtc_server().assign_channel(client->rtc_client_id, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now disconnect the target itself */
|
/* now disconnect the target itself */
|
||||||
|
@ -473,9 +469,17 @@ void VirtualServer::client_move(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(s_source_channel)
|
if(s_source_channel) {
|
||||||
s_source_channel->unregister_client(target);
|
s_source_channel->unregister_client(target);
|
||||||
|
}
|
||||||
s_target_channel->register_client(target);
|
s_target_channel->register_client(target);
|
||||||
|
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
|
||||||
|
this->rtc_server().assign_channel(client->rtc_client_id, s_target_channel->rtc_channel_id);
|
||||||
|
}
|
||||||
|
if(auto client{dynamic_pointer_cast<VoiceClient>(target)}; client) {
|
||||||
|
/* Start normal broadcasting, what the client expects */
|
||||||
|
this->rtc_server().start_broadcast(client->rtc_client_id, 1, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* client left the server */
|
/* client left the server */
|
||||||
if(target->currentChannel) {
|
if(target->currentChannel) {
|
||||||
|
@ -489,6 +493,9 @@ void VirtualServer::client_move(
|
||||||
}
|
}
|
||||||
|
|
||||||
s_source_channel->unregister_client(target);
|
s_source_channel->unregister_client(target);
|
||||||
|
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
|
||||||
|
this->rtc_server().assign_channel(client->rtc_client_id, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TIMING_STEP(timings, "notify view");
|
TIMING_STEP(timings, "notify view");
|
||||||
|
|
|
@ -208,7 +208,7 @@ void VirtualServer::executeServerTick() {
|
||||||
{
|
{
|
||||||
BEGIN_TIMINGS();
|
BEGIN_TIMINGS();
|
||||||
|
|
||||||
this->serverStatistics->tick();
|
this->server_statistics_->tick();
|
||||||
{
|
{
|
||||||
lock_guard<threads::Mutex> lock(this->join_attempts_lock);
|
lock_guard<threads::Mutex> lock(this->join_attempts_lock);
|
||||||
if(system_clock::now() > this->join_last_decrease + seconds(5)) {
|
if(system_clock::now() > this->join_last_decrease + seconds(5)) {
|
||||||
|
@ -248,7 +248,7 @@ void VirtualServer::executeServerTick() {
|
||||||
BEGIN_TIMINGS();
|
BEGIN_TIMINGS();
|
||||||
if(this->conversation_cache_cleanup_timestamp + minutes(15) < system_clock::now()) {
|
if(this->conversation_cache_cleanup_timestamp + minutes(15) < system_clock::now()) {
|
||||||
debugMessage(this->serverId, "Cleaning up conversation cache.");
|
debugMessage(this->serverId, "Cleaning up conversation cache.");
|
||||||
this->_conversation_manager->cleanup_cache();
|
this->conversation_manager_->cleanup_cache();
|
||||||
conversation_cache_cleanup_timestamp = system_clock::now();
|
conversation_cache_cleanup_timestamp = system_clock::now();
|
||||||
}
|
}
|
||||||
END_TIMINGS(timing_ccache);
|
END_TIMINGS(timing_ccache);
|
||||||
|
@ -256,7 +256,7 @@ void VirtualServer::executeServerTick() {
|
||||||
|
|
||||||
{
|
{
|
||||||
BEGIN_TIMINGS();
|
BEGIN_TIMINGS();
|
||||||
this->musicManager->execute_tick();
|
this->music_manager_->execute_tick();
|
||||||
END_TIMINGS(music_manager);
|
END_TIMINGS(music_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "InstanceHandler.h"
|
#include "InstanceHandler.h"
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "VirtualServer.h"
|
#include "VirtualServer.h"
|
||||||
|
#include "./rtc/lib.h"
|
||||||
#include "src/manager/ConversationManager.h"
|
#include "src/manager/ConversationManager.h"
|
||||||
#include <misc/sassert.h>
|
#include <misc/sassert.h>
|
||||||
#include <src/manager/ActionLogger.h>
|
#include <src/manager/ActionLogger.h>
|
||||||
|
@ -48,6 +49,8 @@ VirtualServer::VirtualServer(uint16_t serverId, sql::SqlManager* database) : ser
|
||||||
bool VirtualServer::initialize(bool test_properties) {
|
bool VirtualServer::initialize(bool test_properties) {
|
||||||
assert(self.lock());
|
assert(self.lock());
|
||||||
|
|
||||||
|
this->rtc_server_ = std::make_unique<rtc::Server>();
|
||||||
|
|
||||||
this->_properties = serverInstance->databaseHelper()->loadServerProperties(self.lock());
|
this->_properties = serverInstance->databaseHelper()->loadServerProperties(self.lock());
|
||||||
this->_properties->registerNotifyHandler([&](Property& prop){
|
this->_properties->registerNotifyHandler([&](Property& prop){
|
||||||
if(prop.type() == property::VIRTUALSERVER_DISABLE_IP_SAVING) {
|
if(prop.type() == property::VIRTUALSERVER_DISABLE_IP_SAVING) {
|
||||||
|
@ -179,8 +182,8 @@ bool VirtualServer::initialize(bool test_properties) {
|
||||||
properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(base64::encode(buffer, bufferLength)));
|
properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(base64::encode(buffer, bufferLength)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this->_conversation_manager = make_shared<conversation::ConversationManager>(this->ref());
|
this->conversation_manager_ = make_shared<conversation::ConversationManager>(this->ref());
|
||||||
this->_conversation_manager->initialize(this->_conversation_manager);
|
this->conversation_manager_->initialize(this->conversation_manager_);
|
||||||
|
|
||||||
channelTree = new ServerChannelTree(self.lock(), this->sql);
|
channelTree = new ServerChannelTree(self.lock(), this->sql);
|
||||||
channelTree->loadChannelsFromDatabase();
|
channelTree->loadChannelsFromDatabase();
|
||||||
|
@ -243,7 +246,7 @@ bool VirtualServer::initialize(bool test_properties) {
|
||||||
|
|
||||||
letters = new letter::LetterManager(this);
|
letters = new letter::LetterManager(this);
|
||||||
|
|
||||||
serverStatistics = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics());
|
server_statistics_ = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics());
|
||||||
|
|
||||||
this->serverRoot = std::make_shared<InternalClient>(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as<string>(), false);
|
this->serverRoot = std::make_shared<InternalClient>(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as<string>(), false);
|
||||||
static_pointer_cast<InternalClient>(this->serverRoot)->setSharedLock(this->serverRoot);
|
static_pointer_cast<InternalClient>(this->serverRoot)->setSharedLock(this->serverRoot);
|
||||||
|
@ -286,10 +289,10 @@ bool VirtualServer::initialize(bool test_properties) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->channelTree->printChannelTree([&](std::string msg){ debugMessage(this->serverId, msg); });
|
this->channelTree->printChannelTree([&](std::string msg){ debugMessage(this->serverId, msg); });
|
||||||
this->musicManager = make_shared<music::MusicBotManager>(self.lock());
|
this->music_manager_ = make_shared<music::MusicBotManager>(self.lock());
|
||||||
this->musicManager->_self = this->musicManager;
|
this->music_manager_->_self = this->music_manager_;
|
||||||
this->musicManager->load_playlists();
|
this->music_manager_->load_playlists();
|
||||||
this->musicManager->load_bots();
|
this->music_manager_->load_bots();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if(this->properties()[property::VIRTUALSERVER_ICON_ID] != (IconId) 0)
|
if(this->properties()[property::VIRTUALSERVER_ICON_ID] != (IconId) 0)
|
||||||
|
@ -316,7 +319,7 @@ bool VirtualServer::initialize(bool test_properties) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* lets cleanup the conversations for not existent channels */
|
/* lets cleanup the conversations for not existent channels */
|
||||||
this->_conversation_manager->synchronize_channels();
|
this->conversation_manager_->synchronize_channels();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +330,7 @@ VirtualServer::~VirtualServer() {
|
||||||
delete this->channelTree;
|
delete this->channelTree;
|
||||||
delete this->letters;
|
delete this->letters;
|
||||||
delete this->complains;
|
delete this->complains;
|
||||||
this->_conversation_manager.reset();
|
this->conversation_manager_.reset();
|
||||||
|
|
||||||
if(this->_serverKey) ecc_free(this->_serverKey);
|
if(this->_serverKey) ecc_free(this->_serverKey);
|
||||||
delete this->_serverKey;
|
delete this->_serverKey;
|
||||||
|
@ -511,8 +514,8 @@ bool VirtualServer::start(std::string& error) {
|
||||||
properties()[property::VIRTUALSERVER_UPTIME] = 0;
|
properties()[property::VIRTUALSERVER_UPTIME] = 0;
|
||||||
this->startTimestamp = system_clock::now();
|
this->startTimestamp = system_clock::now();
|
||||||
|
|
||||||
this->musicManager->cleanup_semi_bots();
|
this->music_manager_->cleanup_semi_bots();
|
||||||
this->musicManager->connectBots();
|
this->music_manager_->connectBots();
|
||||||
|
|
||||||
{
|
{
|
||||||
threads::MutexLock lock(this->stateLock);
|
threads::MutexLock lock(this->stateLock);
|
||||||
|
@ -585,7 +588,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) {
|
||||||
logError(this->serverId, "Got client with unknown type: " + to_string(cl->getType()));
|
logError(this->serverId, "Got client with unknown type: " + to_string(cl->getType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->musicManager->disconnectBots();
|
this->music_manager_->disconnectBots();
|
||||||
|
|
||||||
serverInstance->cancelExecute(this);
|
serverInstance->cancelExecute(this);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,10 @@ namespace ts {
|
||||||
class MusicBotManager;
|
class MusicBotManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
class Server;
|
||||||
|
}
|
||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
class ConnectedClient;
|
class ConnectedClient;
|
||||||
class VoiceClient;
|
class VoiceClient;
|
||||||
|
@ -184,6 +188,7 @@ namespace ts {
|
||||||
inline ServerId getServerId(){ return this->serverId; }
|
inline ServerId getServerId(){ return this->serverId; }
|
||||||
inline ServerChannelTree* getChannelTree(){ return this->channelTree; }
|
inline ServerChannelTree* getChannelTree(){ return this->channelTree; }
|
||||||
inline GroupManager* getGroupManager() { return this->groups; }
|
inline GroupManager* getGroupManager() { return this->groups; }
|
||||||
|
inline rtc::Server& rtc_server() { return *this->rtc_server_; }
|
||||||
|
|
||||||
[[nodiscard]] inline auto getTokenManager() -> token::TokenManager* {
|
[[nodiscard]] inline auto getTokenManager() -> token::TokenManager* {
|
||||||
return this->tokenManager;
|
return this->tokenManager;
|
||||||
|
@ -210,7 +215,7 @@ namespace ts {
|
||||||
|
|
||||||
std::string getDisplayName(){ return properties()[property::VIRTUALSERVER_NAME]; }
|
std::string getDisplayName(){ return properties()[property::VIRTUALSERVER_NAME]; }
|
||||||
|
|
||||||
std::shared_ptr<stats::ConnectionStatistics> getServerStatistics(){ return serverStatistics; }
|
std::shared_ptr<stats::ConnectionStatistics> getServerStatistics(){ return server_statistics_; }
|
||||||
|
|
||||||
std::shared_ptr<VoiceServer> getVoiceServer(){ return this->udpVoiceServer; }
|
std::shared_ptr<VoiceServer> getVoiceServer(){ return this->udpVoiceServer; }
|
||||||
WebControlServer* getWebServer(){ return this->webControlServer; }
|
WebControlServer* getWebServer(){ return this->webControlServer; }
|
||||||
|
@ -281,7 +286,7 @@ namespace ts {
|
||||||
void send_text_message(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* sender */, const std::string& /* message */);
|
void send_text_message(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* sender */, const std::string& /* message */);
|
||||||
|
|
||||||
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
|
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
|
||||||
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->_conversation_manager; }
|
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->conversation_manager_; }
|
||||||
|
|
||||||
inline auto& get_channel_tree_lock() { return this->channel_tree_lock; }
|
inline auto& get_channel_tree_lock() { return this->channel_tree_lock; }
|
||||||
|
|
||||||
|
@ -305,9 +310,10 @@ namespace ts {
|
||||||
token::TokenManager* tokenManager = nullptr;
|
token::TokenManager* tokenManager = nullptr;
|
||||||
ComplainManager* complains = nullptr;
|
ComplainManager* complains = nullptr;
|
||||||
letter::LetterManager* letters = nullptr;
|
letter::LetterManager* letters = nullptr;
|
||||||
std::shared_ptr<music::MusicBotManager> musicManager;
|
std::shared_ptr<music::MusicBotManager> music_manager_;
|
||||||
std::shared_ptr<stats::ConnectionStatistics> serverStatistics;
|
std::shared_ptr<stats::ConnectionStatistics> server_statistics_;
|
||||||
std::shared_ptr<conversation::ConversationManager> _conversation_manager;
|
std::shared_ptr<conversation::ConversationManager> conversation_manager_;
|
||||||
|
std::unique_ptr<rtc::Server> rtc_server_;
|
||||||
|
|
||||||
sql::SqlManager* sql;
|
sql::SqlManager* sql;
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,7 @@ shared_ptr<VirtualServer> VirtualServerManager::create_server(std::string hosts,
|
||||||
server->properties()[property::VIRTUALSERVER_HOST] = hosts;
|
server->properties()[property::VIRTUALSERVER_HOST] = hosts;
|
||||||
server->properties()[property::VIRTUALSERVER_PORT] = port;
|
server->properties()[property::VIRTUALSERVER_PORT] = port;
|
||||||
if(config::server::default_music_bot) {
|
if(config::server::default_music_bot) {
|
||||||
auto bot = server->musicManager->createBot(0);
|
auto bot = server->music_manager_->createBot(0);
|
||||||
if(!bot) {
|
if(!bot) {
|
||||||
logCritical(server->getServerId(), "Failed to create default music bot!");
|
logCritical(server->getServerId(), "Failed to create default music bot!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,8 @@ using namespace std;
|
||||||
using namespace ts;
|
using namespace ts;
|
||||||
using namespace ts::server;
|
using namespace ts::server;
|
||||||
|
|
||||||
extern InstanceHandler* serverInstance;
|
ServerChannel::ServerChannel(uint32_t rtc_channel_id, ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId),
|
||||||
|
rtc_channel_id{rtc_channel_id} {
|
||||||
ServerChannel::ServerChannel(ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId) {
|
|
||||||
memtrack::allocated<ServerChannel>(this);
|
memtrack::allocated<ServerChannel>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,18 +58,19 @@ void ServerChannel::setProperties(const std::shared_ptr<Properties> &ptr) {
|
||||||
BasicChannel::setProperties(ptr);
|
BasicChannel::setProperties(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerChannelTree::ServerChannelTree(const std::shared_ptr<server::VirtualServer>& server, sql::SqlManager* sql) : sql(sql), server(server) { }
|
ServerChannelTree::ServerChannelTree(const std::shared_ptr<server::VirtualServer>& server, sql::SqlManager* sql) : sql(sql), server_ref(server) { }
|
||||||
|
|
||||||
ServerChannelTree::~ServerChannelTree() { }
|
ServerChannelTree::~ServerChannelTree() { }
|
||||||
|
|
||||||
void ServerChannelTree::deleteSemiPermanentChannels() {
|
void ServerChannelTree::deleteSemiPermanentChannels() {
|
||||||
loop:
|
loop:
|
||||||
|
|
||||||
for(const auto& ch : this->channels())
|
for(const auto& ch : this->channels()) {
|
||||||
if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels
|
if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels
|
||||||
this->delete_channel_root(ch);
|
this->delete_channel_root(ch);
|
||||||
goto loop;
|
goto loop;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelId ServerChannelTree::generateChannelId() {
|
ChannelId ServerChannelTree::generateChannelId() {
|
||||||
|
@ -90,12 +90,15 @@ std::shared_ptr<BasicChannel> ServerChannelTree::createChannel(ChannelId parentI
|
||||||
std::shared_ptr<BasicChannel> channel = BasicChannelTree::createChannel(parentId, orderId, name);
|
std::shared_ptr<BasicChannel> channel = BasicChannelTree::createChannel(parentId, orderId, name);
|
||||||
if(!channel) return channel;
|
if(!channel) return channel;
|
||||||
|
|
||||||
auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server.lock(), channel->channelId());
|
auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server_ref.lock(), channel->channelId());
|
||||||
for(const auto& prop : channel->properties().list_properties())
|
for(const auto& prop : channel->properties().list_properties()) {
|
||||||
if(prop.isModified()) //Copy the already set properties
|
if(prop.isModified()) { //Copy the already set properties
|
||||||
(*properties)[prop.type()] = prop.value();
|
(*properties)[prop.type()] = prop.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static_pointer_cast<ServerChannel>(channel)->setProperties(properties);
|
static_pointer_cast<ServerChannel>(channel)->setProperties(properties);
|
||||||
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server.lock(), channel->channelId()));
|
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server_ref.lock(), channel->channelId()));
|
||||||
|
|
||||||
channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
||||||
channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
@ -121,7 +124,7 @@ inline std::shared_ptr<TreeView::LinkedTreeEntry> findLinkedChannelByPool(const
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerId ServerChannelTree::getServerId() {
|
ServerId ServerChannelTree::getServerId() {
|
||||||
auto s = this->server.lock();
|
auto s = this->server_ref.lock();
|
||||||
return s ? s->getServerId() : 0UL;
|
return s ? s->getServerId() : 0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,8 +535,16 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column)
|
||||||
if(channelId == 0)
|
if(channelId == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
auto channel = std::make_shared<ServerChannel>(parentId, channelId);
|
auto server = this->server_ref.lock();
|
||||||
auto server = this->server.lock();
|
|
||||||
|
std::shared_ptr<ServerChannel> channel;
|
||||||
|
if(server) {
|
||||||
|
auto rtc_channel_id = server->rtc_server().create_channel();
|
||||||
|
|
||||||
|
channel = std::make_shared<ServerChannel>(rtc_channel_id, parentId, channelId);
|
||||||
|
} else {
|
||||||
|
channel = std::make_shared<ServerChannel>(0, parentId, channelId);
|
||||||
|
}
|
||||||
static_pointer_cast<ServerChannel>(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId));
|
static_pointer_cast<ServerChannel>(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId));
|
||||||
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId()));
|
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId()));
|
||||||
|
|
||||||
|
@ -544,24 +555,30 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column)
|
||||||
}
|
}
|
||||||
|
|
||||||
deque<ChannelId> ServerChannelTree::deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel) {
|
deque<ChannelId> ServerChannelTree::deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel) {
|
||||||
auto server = this->server.lock();
|
auto server = this->server_ref.lock();
|
||||||
|
|
||||||
auto channels = this->delete_channel_root(channel);
|
auto channels = this->delete_channel_root(channel);
|
||||||
deque<ChannelId> channel_ids;
|
deque<ChannelId> channel_ids;
|
||||||
for(const auto& channel : channels)
|
for(const auto& channel : channels) {
|
||||||
channel_ids.push_back(channel->channelId());
|
channel_ids.push_back(channel->channelId());
|
||||||
|
}
|
||||||
return channel_ids;
|
return channel_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel> &channel) {
|
void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel> &channel) {
|
||||||
BasicChannelTree::on_channel_entry_deleted(channel);
|
BasicChannelTree::on_channel_entry_deleted(channel);
|
||||||
|
|
||||||
auto server = this->server.lock();
|
auto server_channel = dynamic_pointer_cast<ServerChannel>(channel);
|
||||||
|
assert(server_channel);
|
||||||
|
|
||||||
|
auto server = this->server_ref.lock();
|
||||||
if(server) {
|
if(server) {
|
||||||
server->getGroupManager()->handleChannelDeleted(channel->channelId());
|
server->getGroupManager()->handleChannelDeleted(channel->channelId());
|
||||||
server->conversation_manager()->delete_conversation(channel->channelId());
|
server->conversation_manager()->delete_conversation(channel->channelId());
|
||||||
} else
|
server->rtc_server().destroy_channel(server_channel->rtc_channel_id);
|
||||||
|
} else {
|
||||||
serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId());
|
serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
|
auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
|
||||||
|
@ -570,11 +587,18 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
|
||||||
sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute();
|
sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute();
|
||||||
LOG_SQL_CMD(sql_result);
|
LOG_SQL_CMD(sql_result);
|
||||||
|
|
||||||
serverInstance->databaseHelper()->deleteChannelPermissions(this->server.lock(), channel->channelId());
|
serverInstance->databaseHelper()->deleteChannelPermissions(this->server_ref.lock(), channel->channelId());
|
||||||
sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
|
sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
|
||||||
LOG_SQL_CMD(sql_result);
|
LOG_SQL_CMD(sql_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<BasicChannel> ServerChannelTree::allocateChannel(const shared_ptr<BasicChannel> &parent, ChannelId channelId) {
|
std::shared_ptr<BasicChannel> ServerChannelTree::allocateChannel(const shared_ptr<BasicChannel> &parent, ChannelId channelId) {
|
||||||
return std::make_shared<ServerChannel>(parent ? parent->channelId() : 0, channelId);
|
auto server = this->server_ref.lock();
|
||||||
|
if(server) {
|
||||||
|
auto rtc_channel_id = server->rtc_server().create_channel();
|
||||||
|
|
||||||
|
return std::make_shared<ServerChannel>(rtc_channel_id, parent->channelId(), channelId);
|
||||||
|
} else {
|
||||||
|
return std::make_shared<ServerChannel>(0, parent->channelId(), channelId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include "Properties.h"
|
#include "Properties.h"
|
||||||
#include "PermissionManager.h"
|
#include "PermissionManager.h"
|
||||||
#include "BasicChannel.h"
|
#include "BasicChannel.h"
|
||||||
#include "../Group.h"
|
#include "../Group.h"
|
||||||
|
#include "../rtc/lib.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sql/SqlQuery.h>
|
#include <sql/SqlQuery.h>
|
||||||
|
|
||||||
|
@ -19,11 +19,13 @@ namespace ts {
|
||||||
class ServerChannel : public BasicChannel {
|
class ServerChannel : public BasicChannel {
|
||||||
friend class ServerChannelTree;
|
friend class ServerChannelTree;
|
||||||
public:
|
public:
|
||||||
ServerChannel(ChannelId parentId, ChannelId channelId);
|
ServerChannel(uint32_t rtc_channel_id, ChannelId parentId, ChannelId channelId);
|
||||||
|
~ServerChannel() override;
|
||||||
|
|
||||||
~ServerChannel();
|
|
||||||
void setProperties(const std::shared_ptr<Properties> &ptr) override;
|
void setProperties(const std::shared_ptr<Properties> &ptr) override;
|
||||||
|
|
||||||
|
uint32_t rtc_channel_id;
|
||||||
|
|
||||||
std::shared_mutex client_lock;
|
std::shared_mutex client_lock;
|
||||||
std::deque<std::weak_ptr<server::ConnectedClient>> clients;
|
std::deque<std::weak_ptr<server::ConnectedClient>> clients;
|
||||||
|
|
||||||
|
@ -37,24 +39,24 @@ namespace ts {
|
||||||
class ServerChannelTree : public BasicChannelTree {
|
class ServerChannelTree : public BasicChannelTree {
|
||||||
public:
|
public:
|
||||||
ServerChannelTree(const std::shared_ptr<server::VirtualServer>&, sql::SqlManager*);
|
ServerChannelTree(const std::shared_ptr<server::VirtualServer>&, sql::SqlManager*);
|
||||||
virtual ~ServerChannelTree();
|
~ServerChannelTree() override;
|
||||||
void loadChannelsFromDatabase();
|
void loadChannelsFromDatabase();
|
||||||
|
|
||||||
virtual std::shared_ptr<BasicChannel> createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override;
|
std::shared_ptr<BasicChannel> createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override;
|
||||||
virtual std::deque<ChannelId> deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel);
|
virtual std::deque<ChannelId> deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel);
|
||||||
|
|
||||||
void deleteSemiPermanentChannels();
|
void deleteSemiPermanentChannels();
|
||||||
|
|
||||||
std::shared_ptr<LinkedTreeEntry> tree_head() { return this->head; }
|
std::shared_ptr<LinkedTreeEntry> tree_head() { return this->head; }
|
||||||
protected:
|
protected:
|
||||||
virtual ChannelId generateChannelId() override;
|
ChannelId generateChannelId() override;
|
||||||
|
|
||||||
virtual void on_channel_entry_deleted(const std::shared_ptr<BasicChannel> &channel) override;
|
void on_channel_entry_deleted(const std::shared_ptr<BasicChannel> &channel) override;
|
||||||
|
|
||||||
std::shared_ptr<BasicChannel> allocateChannel(const std::shared_ptr<BasicChannel> &parent, ChannelId channelId) override;
|
std::shared_ptr<BasicChannel> allocateChannel(const std::shared_ptr<BasicChannel> &parent, ChannelId channelId) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::weak_ptr<server::VirtualServer> server;
|
std::weak_ptr<server::VirtualServer> server_ref;
|
||||||
ServerId getServerId();
|
ServerId getServerId();
|
||||||
sql::SqlManager* sql;
|
sql::SqlManager* sql;
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,10 @@ namespace ts {
|
||||||
/** Notifies (after request) */
|
/** Notifies (after request) */
|
||||||
bool sendNeededPermissions(bool /* force an update */); /* invoke this because it dosn't spam the client */
|
bool sendNeededPermissions(bool /* force an update */); /* invoke this because it dosn't spam the client */
|
||||||
virtual bool notifyClientNeededPermissions();
|
virtual bool notifyClientNeededPermissions();
|
||||||
virtual bool notifyServerGroupList();
|
virtual bool notifyServerGroupList(bool as_notify = true);
|
||||||
virtual bool notifyGroupPermList(const std::shared_ptr<Group>&, bool);
|
virtual bool notifyGroupPermList(const std::shared_ptr<Group>&, bool);
|
||||||
virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr<permission::v2::PermissionManager>&, bool);
|
virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr<permission::v2::PermissionManager>&, bool);
|
||||||
virtual bool notifyChannelGroupList();
|
virtual bool notifyChannelGroupList(bool as_notify = true);
|
||||||
virtual bool notifyConnectionInfo(const std::shared_ptr<ConnectedClient> &target, const std::shared_ptr<ConnectionInfoData> &info);
|
virtual bool notifyConnectionInfo(const std::shared_ptr<ConnectedClient> &target, const std::shared_ptr<ConnectionInfoData> &info);
|
||||||
virtual bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
|
virtual bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
|
||||||
virtual bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
|
virtual bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
|
||||||
|
|
|
@ -46,8 +46,8 @@ do { \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
bool ConnectedClient::notifyServerGroupList() {
|
bool ConnectedClient::notifyServerGroupList(bool as_notify) {
|
||||||
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergrouplist" : "");
|
Command cmd(as_notify ? "notifyservergrouplist" : "");
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) {
|
for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) {
|
||||||
|
@ -173,8 +173,8 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectedClient::notifyChannelGroupList() {
|
bool ConnectedClient::notifyChannelGroupList(bool as_notify) {
|
||||||
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : "");
|
Command cmd(as_notify ? "notifychannelgrouplist" : "");
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) {
|
for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) {
|
||||||
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
|
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
|
||||||
|
|
|
@ -196,7 +196,7 @@ bool ConnectedClient::handle_text_command(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto botId = static_cast<ClientDbId>(stoll(arguments[1]));
|
auto botId = static_cast<ClientDbId>(stoll(arguments[1]));
|
||||||
auto bot = this->server->musicManager->findBotById(botId);
|
auto bot = this->server->music_manager_->findBotById(botId);
|
||||||
if (!bot) ERR(serverInstance->musicRoot(), "Could not find target bot");
|
if (!bot) ERR(serverInstance->musicRoot(), "Could not find target bot");
|
||||||
if(bot->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId() &&
|
if(bot->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId() &&
|
||||||
!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_channel_list, this->getChannelId())) &&
|
!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_channel_list, this->getChannelId())) &&
|
||||||
|
@ -230,7 +230,7 @@ bool ConnectedClient::handle_text_command(
|
||||||
} else if (TARG(0, "delete")) {
|
} else if (TARG(0, "delete")) {
|
||||||
GBOT(bot, true);
|
GBOT(bot, true);
|
||||||
PERM_CHECK_BOT(i_client_music_delete_power, i_client_music_needed_delete_power, "You don't have the permission to rename this music bot");
|
PERM_CHECK_BOT(i_client_music_delete_power, i_client_music_needed_delete_power, "You don't have the permission to rename this music bot");
|
||||||
this->server->musicManager->deleteBot(bot);
|
this->server->music_manager_->deleteBot(bot);
|
||||||
send_message(bot, "You successfully deleted this music bot!");
|
send_message(bot, "You successfully deleted this music bot!");
|
||||||
return true;
|
return true;
|
||||||
} else if(TARG(0, "yt") || TARG(0, "soundcloud") || TARG(0, "sc")){
|
} else if(TARG(0, "yt") || TARG(0, "soundcloud") || TARG(0, "sc")){
|
||||||
|
|
|
@ -26,6 +26,17 @@ constexpr static auto kMaxWhisperClientNameLength{30};
|
||||||
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
|
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
|
||||||
constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength};
|
constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength};
|
||||||
|
|
||||||
|
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b) {
|
||||||
|
speak_begin = std::chrono::system_clock::now();
|
||||||
|
speak_last_packet = std::chrono::system_clock::now();
|
||||||
|
};
|
||||||
|
|
||||||
|
SpeakingClient::~SpeakingClient() {
|
||||||
|
if(auto server{this->server}; this->rtc_client_id > 0 && server) {
|
||||||
|
server->rtc_server().destroy_client(this->rtc_client_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
|
bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
|
||||||
//if(this->properties()[property::CLIENT_AWAY].as<bool>()) return false;
|
//if(this->properties()[property::CLIENT_AWAY].as<bool>()) return false;
|
||||||
if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as<bool>()) return false;
|
if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as<bool>()) return false;
|
||||||
|
@ -47,76 +58,14 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedCl
|
||||||
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false);
|
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) {
|
bool SpeakingClient::should_handle_voice_packet(size_t) {
|
||||||
auto server = this->getServer();
|
|
||||||
auto self = _this.lock();
|
|
||||||
if(!self || !server) return;
|
|
||||||
|
|
||||||
if(data.length() < 3) {
|
|
||||||
this->disconnect("invalid packet (Voice; Length: " + to_string(data.length()) + ")");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if(rand() % 10 == 0) {
|
|
||||||
logMessage(0, "Dropping audio packet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logMessage(0, "Received voice: Head: {} Fragmented: {}, length: {}", head, fragmented, data.length());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto current_channel = this->currentChannel;
|
auto current_channel = this->currentChannel;
|
||||||
if(!current_channel) { return; }
|
if(!current_channel) { return false; }
|
||||||
if(!this->allowedToTalk) { return; }
|
if(!this->allowedToTalk) { return false; }
|
||||||
this->updateSpeak(false, system_clock::now());
|
this->updateSpeak(false, system_clock::now());
|
||||||
this->resetIdleTime();
|
this->resetIdleTime();
|
||||||
|
|
||||||
auto target_clients = this->server->getClientsByChannel(current_channel);
|
return true;
|
||||||
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<ConnectedClient>& client) {
|
|
||||||
if(client == this) return true;
|
|
||||||
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(client);
|
|
||||||
if(!speaking_client) return true;
|
|
||||||
|
|
||||||
return !speaking_client->shouldReceiveVoice(self);
|
|
||||||
}), target_clients.end());
|
|
||||||
if(target_clients.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VoicePacketFlags flags{};
|
|
||||||
flags.head = head;
|
|
||||||
flags.fragmented = fragmented;
|
|
||||||
flags.new_protocol = false;
|
|
||||||
{
|
|
||||||
//crypt_mode = 1 | disabled
|
|
||||||
//crypt_mode = 2 | enabled
|
|
||||||
auto crypt_mode = this->server->voice_encryption_mode();
|
|
||||||
if(crypt_mode == 0)
|
|
||||||
flags.encrypted = !current_channel->properties()[property::CHANNEL_CODEC_IS_UNENCRYPTED].as<bool>();
|
|
||||||
else
|
|
||||||
flags.encrypted = crypt_mode == 2;
|
|
||||||
}
|
|
||||||
uint16_t vpacketId = be2le16((char*) data.data_ptr());
|
|
||||||
auto codec = (uint8_t) data[2];
|
|
||||||
#ifdef PKT_LOG_VOICE
|
|
||||||
logTrace(lstream << CLIENT_LOG_PREFIX << "Voice length: " << data.length() << " -> id: " << vpacketId << " codec: " << (int) codec << " head: " << head << " fragmented: " << fragmented);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char buffer[data.length() + 2];
|
|
||||||
|
|
||||||
le2be16(vpacketId, &buffer[0]);
|
|
||||||
le2be16(getClientId(), &buffer[2]);
|
|
||||||
buffer[4] = codec;
|
|
||||||
|
|
||||||
if(data.length() - 3 > 0) {
|
|
||||||
memcpy(&buffer[5], &data[3], data.length() - 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto bview = pipes::buffer_view{buffer, data.length() + 2};
|
|
||||||
for (const auto& client : target_clients) {
|
|
||||||
auto speaking_client = static_pointer_cast<SpeakingClient>(client);
|
|
||||||
speaking_client->send_voice_packet(bview, flags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//2 + 2 + 8
|
//2 + 2 + 8
|
||||||
|
@ -755,6 +704,19 @@ void SpeakingClient::processJoin() {
|
||||||
|
|
||||||
TIMING_STEP(timings, "setup ");
|
TIMING_STEP(timings, "setup ");
|
||||||
ref_server->registerClient(_this.lock());
|
ref_server->registerClient(_this.lock());
|
||||||
|
if(this->rtc_client_id) {
|
||||||
|
/* in case of client reconnect */
|
||||||
|
this->server->rtc_server().destroy_client(this->rtc_client_id);
|
||||||
|
}
|
||||||
|
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) {
|
||||||
|
this->rtc_client_id = this->server->rtc_server().create_native_client(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
||||||
|
} else if(this->getType() == ClientType::CLIENT_TEASPEAK) {
|
||||||
|
/* TODO: Will be a RTP client later on, just without audio */
|
||||||
|
this->rtc_client_id = this->server->rtc_server().create_native_client(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
||||||
|
} else if(this->getType() == ClientType::CLIENT_WEB) {
|
||||||
|
this->rtc_client_id = this->server->rtc_server().create_rtp_client(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
||||||
|
}
|
||||||
|
|
||||||
TIMING_STEP(timings, "server reg ");
|
TIMING_STEP(timings, "server reg ");
|
||||||
ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId());
|
ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId());
|
||||||
TIMING_STEP(timings, "grp cleanup");
|
TIMING_STEP(timings, "grp cleanup");
|
||||||
|
@ -868,7 +830,7 @@ void SpeakingClient::processLeave() {
|
||||||
server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
|
server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
|
||||||
}
|
}
|
||||||
server->groups->disableCache(ownLock->getClientDatabaseId());
|
server->groups->disableCache(ownLock->getClientDatabaseId());
|
||||||
server->musicManager->cleanup_client_bots(this->getClientDatabaseId());
|
server->music_manager_->cleanup_client_bots(this->getClientDatabaseId());
|
||||||
//ref_server = nullptr; Removed caused nullptr exceptions
|
//ref_server = nullptr; Removed caused nullptr exceptions
|
||||||
}
|
}
|
||||||
{ //Delete own viewing clients
|
{ //Delete own viewing clients
|
||||||
|
@ -885,23 +847,29 @@ void SpeakingClient::triggerVoiceEnd() {
|
||||||
this->properties()[property::CLIENT_FLAG_TALKING] = false;
|
this->properties()[property::CLIENT_FLAG_TALKING] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeakingClient::updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &now) {
|
void SpeakingClient::updateSpeak(bool only_update, const std::chrono::system_clock::time_point &now) {
|
||||||
threads::MutexLock lock(this->speak_lock);
|
std::lock_guard speak_lock{this->speak_mutex};
|
||||||
|
|
||||||
if(this->speak_last_packet + this->speak_accuracy < now) {
|
if(this->speak_last_packet + this->speak_accuracy < now) {
|
||||||
if(this->speak_last_packet > this->speak_begin) {
|
if(this->speak_last_packet > this->speak_begin) {
|
||||||
if(!this->properties()[property::CLIENT_FLAG_TALKING].as<bool>())
|
if(!this->properties()[property::CLIENT_FLAG_TALKING].as<bool>()) {
|
||||||
this->properties()[property::CLIENT_FLAG_TALKING] = true;
|
this->properties()[property::CLIENT_FLAG_TALKING] = true;
|
||||||
|
}
|
||||||
|
|
||||||
this->speak_time += duration_cast<milliseconds>(this->speak_last_packet - this->speak_begin);
|
this->speak_time += duration_cast<milliseconds>(this->speak_last_packet - this->speak_begin);
|
||||||
} else {
|
} else {
|
||||||
if(this->properties()[property::CLIENT_FLAG_TALKING].as<bool>())
|
if(this->properties()[property::CLIENT_FLAG_TALKING].as<bool>()) {
|
||||||
this->properties()[property::CLIENT_FLAG_TALKING] = false;
|
this->properties()[property::CLIENT_FLAG_TALKING] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->speak_begin = now;
|
this->speak_begin = now;
|
||||||
this->speak_last_packet = now;
|
this->speak_last_packet = now;
|
||||||
}
|
}
|
||||||
if(!onlyUpdate)
|
|
||||||
|
if(!only_update) {
|
||||||
this->speak_last_packet = now;
|
this->speak_last_packet = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeakingClient::tick(const std::chrono::system_clock::time_point &time) {
|
void SpeakingClient::tick(const std::chrono::system_clock::time_point &time) {
|
||||||
|
@ -930,19 +898,128 @@ command_result SpeakingClient::handleCommand(Command &command) {
|
||||||
if(this->connectionState() == ConnectionState::INIT_HIGH) {
|
if(this->connectionState() == ConnectionState::INIT_HIGH) {
|
||||||
if(this->handshake.state == HandshakeState::BEGIN || this->handshake.state == HandshakeState::IDENTITY_PROOF) {
|
if(this->handshake.state == HandshakeState::BEGIN || this->handshake.state == HandshakeState::IDENTITY_PROOF) {
|
||||||
command_result result;
|
command_result result;
|
||||||
if(command.command() == "handshakebegin")
|
if(command.command() == "handshakebegin") {
|
||||||
result.reset(this->handleCommandHandshakeBegin(command));
|
result.reset(this->handleCommandHandshakeBegin(command));
|
||||||
else if(command.command() == "handshakeindentityproof")
|
} else if(command.command() == "handshakeindentityproof") {
|
||||||
result.reset(this->handleCommandHandshakeIdentityProof(command));
|
result.reset(this->handleCommandHandshakeIdentityProof(command));
|
||||||
else
|
} else {
|
||||||
result.reset(command_result{error::client_not_logged_in});
|
result.reset(command_result{error::client_not_logged_in});
|
||||||
|
}
|
||||||
|
|
||||||
if(result.has_error())
|
if(result.has_error()) {
|
||||||
this->postCommandHandler.push_back([&]{
|
this->postCommandHandler.push_back([&]{
|
||||||
this->close_connection(system_clock::now() + seconds(1));
|
this->close_connection(system_clock::now() + seconds(1));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
} else if(this->connectionState() == ConnectionState::CONNECTED) {
|
||||||
|
if(command.command() == "rtcsessiondescribe") {
|
||||||
|
return this->handleCommandRtcSessionDescribe(command);
|
||||||
|
} else if(command.command() == "rtcicecandidate") {
|
||||||
|
return this->handleCommandRtcIceCandidate(command);
|
||||||
|
} else if(command.command() == "rtcbroadcast") {
|
||||||
|
return this->handleCommandRtcBroadcast(command);
|
||||||
|
} else if(command.command() == "rtcsessionreset") {
|
||||||
|
return this->handleCommandRtcSessionReset(command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ConnectedClient::handleCommand(command);
|
return ConnectedClient::handleCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
command_result SpeakingClient::handleCommandRtcSessionDescribe(Command &command) {
|
||||||
|
CMD_REQ_SERVER;
|
||||||
|
CMD_CHK_AND_INC_FLOOD_POINTS(15);
|
||||||
|
|
||||||
|
uint32_t mode;
|
||||||
|
if(command["mode"].string() == "offer") {
|
||||||
|
mode = 1;
|
||||||
|
} else if(command["mode"].string() == "answer") {
|
||||||
|
mode = 2;
|
||||||
|
} else {
|
||||||
|
return command_result{error::parameter_invalid, "mode"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error{};
|
||||||
|
if(!this->server->rtc_server().apply_remote_description(error, this->rtc_client_id, mode, command["sdp"])) {
|
||||||
|
return command_result{error::vs_critical, error};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode == 1) {
|
||||||
|
std::string result{};
|
||||||
|
if(!this->server->rtc_server().generate_local_description(this->rtc_client_id, result)) {
|
||||||
|
return command_result{error::vs_critical, result};
|
||||||
|
} else {
|
||||||
|
ts::command_builder notify{"notifyrtcsessiondescription"};
|
||||||
|
notify.put_unchecked(0, "mode", "answer");
|
||||||
|
notify.put_unchecked(0, "sdp", result);
|
||||||
|
this->sendCommand(notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return command_result{error::ok};
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result SpeakingClient::handleCommandRtcSessionReset(Command &command) {
|
||||||
|
CMD_REQ_SERVER;
|
||||||
|
CMD_CHK_AND_INC_FLOOD_POINTS(15);
|
||||||
|
|
||||||
|
this->server->rtc_server().reset_rtp_session(this->rtc_client_id);
|
||||||
|
return command_result{error::ok};
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result SpeakingClient::handleCommandRtcIceCandidate(Command &command) {
|
||||||
|
CMD_REQ_SERVER;
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
if(command[0].has("candidate")) {
|
||||||
|
auto candidate = command["candidate"].string();
|
||||||
|
if(!this->server->rtc_server().add_ice_candidate(error, this->rtc_client_id, command["media_line"], candidate)) {
|
||||||
|
return command_result{error::vs_critical, error};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->server->rtc_server().ice_candidates_finished(this->rtc_client_id);
|
||||||
|
}
|
||||||
|
return command_result{error::ok};
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result SpeakingClient::handleCommandRtcBroadcast(Command &command) {
|
||||||
|
CMD_REQ_SERVER;
|
||||||
|
CMD_CHK_AND_INC_FLOOD_POINTS(15);
|
||||||
|
|
||||||
|
/* TODO: Filter out duplicates */
|
||||||
|
|
||||||
|
std::vector<std::tuple<uint8_t, uint32_t>> broadcasts{};
|
||||||
|
broadcasts.reserve(command.bulkCount());
|
||||||
|
|
||||||
|
for(size_t index{0}; index < command.bulkCount(); index++) {
|
||||||
|
auto& bulk = command[index];
|
||||||
|
broadcasts.push_back(std::make_tuple(bulk["type"], bulk.has("ssrc") ? bulk["ssrc"].as<uint32_t>() : (uint32_t) 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
ts::command_result_bulk result{};
|
||||||
|
for(size_t index{0}; index < command.bulkCount(); index++) {
|
||||||
|
auto broadcast_result = this->server->rtc_server().start_broadcast(this->rtc_client_id, std::get<0>(broadcasts[index]), std::get<1>(broadcasts[index]));
|
||||||
|
switch(broadcast_result) {
|
||||||
|
case rtc::BroadcastStartResult::Success:
|
||||||
|
result.emplace_result(error::ok);
|
||||||
|
break;
|
||||||
|
case rtc::BroadcastStartResult::InvalidBroadcastType:
|
||||||
|
result.emplace_result(error::parameter_invalid, "type");
|
||||||
|
break;
|
||||||
|
case rtc::BroadcastStartResult::InvalidStreamId:
|
||||||
|
result.emplace_result(error::rtc_missing_target_channel);
|
||||||
|
break;
|
||||||
|
case rtc::BroadcastStartResult::ClientHasNoChannel:
|
||||||
|
result.emplace_result(error::vs_critical, "no channel");
|
||||||
|
break;
|
||||||
|
case rtc::BroadcastStartResult::InvalidClient:
|
||||||
|
result.emplace_result(error::vs_critical, "invalid client");
|
||||||
|
break;
|
||||||
|
case rtc::BroadcastStartResult::UnknownError:
|
||||||
|
default:
|
||||||
|
result.emplace_result(error::vs_critical, "unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ts::command_result{std::move(result)};
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <json/json.h>
|
|
||||||
#include "ConnectedClient.h"
|
#include "ConnectedClient.h"
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <src/rtc/lib.h>
|
||||||
|
|
||||||
namespace ts::server {
|
namespace ts::server {
|
||||||
class VirtualServer;
|
class VirtualServer;
|
||||||
|
@ -31,11 +32,8 @@ namespace ts::server {
|
||||||
UNSET = 0xff
|
UNSET = 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
|
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b);
|
||||||
speak_begin = std::chrono::system_clock::now();
|
~SpeakingClient() override;
|
||||||
speak_last_packet = std::chrono::system_clock::now();
|
|
||||||
};
|
|
||||||
~SpeakingClient() override = default;
|
|
||||||
|
|
||||||
//Voice
|
//Voice
|
||||||
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
||||||
|
@ -53,7 +51,6 @@ namespace ts::server {
|
||||||
protected:
|
protected:
|
||||||
void tick(const std::chrono::system_clock::time_point &time) override;
|
void tick(const std::chrono::system_clock::time_point &time) override;
|
||||||
|
|
||||||
protected:
|
|
||||||
public:
|
public:
|
||||||
void updateChannelClientProperties(bool channel_lock, bool notify) override;
|
void updateChannelClientProperties(bool channel_lock, bool notify) override;
|
||||||
|
|
||||||
|
@ -61,22 +58,27 @@ namespace ts::server {
|
||||||
command_result handleCommand(Command &command) override;
|
command_result handleCommand(Command &command) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
|
bool should_handle_voice_packet(size_t /* size */);
|
||||||
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */, bool /* head */);
|
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */, bool /* head */);
|
||||||
|
|
||||||
void processJoin();
|
virtual void processJoin();
|
||||||
void processLeave();
|
void processLeave();
|
||||||
|
|
||||||
virtual command_result handleCommandHandshakeBegin(Command&);
|
virtual command_result handleCommandHandshakeBegin(Command&);
|
||||||
virtual command_result handleCommandHandshakeIdentityProof(Command &);
|
virtual command_result handleCommandHandshakeIdentityProof(Command &);
|
||||||
virtual command_result handleCommandClientInit(Command&);
|
virtual command_result handleCommandClientInit(Command&);
|
||||||
|
|
||||||
|
virtual command_result handleCommandRtcSessionDescribe(Command &command);
|
||||||
|
virtual command_result handleCommandRtcSessionReset(Command &command);
|
||||||
|
virtual command_result handleCommandRtcIceCandidate(Command &);
|
||||||
|
virtual command_result handleCommandRtcBroadcast(Command &);
|
||||||
|
|
||||||
void triggerVoiceEnd();
|
void triggerVoiceEnd();
|
||||||
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
|
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
|
||||||
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
|
std::chrono::milliseconds speak_accuracy{1000};
|
||||||
|
|
||||||
threads::Mutex speak_lock;
|
std::mutex speak_mutex;
|
||||||
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
|
std::chrono::milliseconds speak_time{0};
|
||||||
std::chrono::system_clock::time_point speak_begin;
|
std::chrono::system_clock::time_point speak_begin;
|
||||||
std::chrono::system_clock::time_point speak_last_packet;
|
std::chrono::system_clock::time_point speak_last_packet;
|
||||||
|
|
||||||
|
@ -94,5 +96,7 @@ namespace ts::server {
|
||||||
//TeaSpeak
|
//TeaSpeak
|
||||||
std::shared_ptr<Json::Value> identityData;
|
std::shared_ptr<Json::Value> identityData;
|
||||||
} handshake;
|
} handshake;
|
||||||
|
|
||||||
|
rtc::RTCClientId rtc_client_id{0};
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -421,7 +421,7 @@ command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1);
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1);
|
||||||
|
|
||||||
this->notifyChannelGroupList();
|
this->notifyChannelGroupList(this->getType() != ClientType::CLIENT_QUERY);
|
||||||
this->command_times.servergrouplist = system_clock::now();
|
this->command_times.servergrouplist = system_clock::now();
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
}
|
}
|
||||||
|
@ -1036,6 +1036,10 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
|
||||||
}
|
}
|
||||||
} else if (key == "channel_codec") {
|
} else if (key == "channel_codec") {
|
||||||
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec, 1, channel_id);
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec, 1, channel_id);
|
||||||
|
auto value = cmd[key].as<uint32_t>();
|
||||||
|
if(!(value >= 4 && value <= 5)) {
|
||||||
|
return command_result{error::parameter_invalid, "channel_codec"};
|
||||||
|
}
|
||||||
} else if (key == "channel_codec_quality") {
|
} else if (key == "channel_codec_quality") {
|
||||||
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec_quality, 1, channel_id);
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec_quality, 1, channel_id);
|
||||||
} else if (key == "channel_codec_is_unencrypted") {
|
} else if (key == "channel_codec_is_unencrypted") {
|
||||||
|
|
|
@ -2857,6 +2857,7 @@ command_result ConnectedClient::handleCommandListFeatureSupport(ts::Command &cmd
|
||||||
REGISTER_FEATURE("advanced-channel-chat", FeatureSupportMode::FULL, 1);
|
REGISTER_FEATURE("advanced-channel-chat", FeatureSupportMode::FULL, 1);
|
||||||
REGISTER_FEATURE("log-query", FeatureSupportMode::FULL, 1);
|
REGISTER_FEATURE("log-query", FeatureSupportMode::FULL, 1);
|
||||||
REGISTER_FEATURE("whisper-echo", FeatureSupportMode::FULL, 1);
|
REGISTER_FEATURE("whisper-echo", FeatureSupportMode::FULL, 1);
|
||||||
|
REGISTER_FEATURE("video", FeatureSupportMode::EXPERIMENTAL, 1);
|
||||||
|
|
||||||
this->sendCommand(notify);
|
this->sendCommand(notify);
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
|
|
|
@ -57,7 +57,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){
|
if(this->server->music_manager_->max_bots() != -1 && this->server->music_manager_->max_bots() <= this->server->music_manager_->current_bot_count()){
|
||||||
if(config::license->isPremium())
|
if(config::license->isPremium())
|
||||||
return command_result{error::music_limit_reached};
|
return command_result{error::music_limit_reached};
|
||||||
else
|
else
|
||||||
|
@ -79,7 +79,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
|
||||||
|
|
||||||
auto max_bots = permissions[permission::i_client_music_limit];
|
auto max_bots = permissions[permission::i_client_music_limit];
|
||||||
if(max_bots.has_value) {
|
if(max_bots.has_value) {
|
||||||
auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId());
|
auto ownBots = this->server->music_manager_->listBots(this->getClientDatabaseId());
|
||||||
if(!permission::v2::permission_granted(ownBots.size() + 1, max_bots))
|
if(!permission::v2::permission_granted(ownBots.size() + 1, max_bots))
|
||||||
return command_result{error::music_client_limit_reached};
|
return command_result{error::music_client_limit_reached};
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
|
||||||
if(!channel)
|
if(!channel)
|
||||||
channel = this->server->channelTree->getDefaultChannel();
|
channel = this->server->channelTree->getDefaultChannel();
|
||||||
|
|
||||||
auto bot = this->server->musicManager->createBot(this->getClientDatabaseId());
|
auto bot = this->server->music_manager_->createBot(this->getClientDatabaseId());
|
||||||
if(!bot) return command_result{error::vs_critical};
|
if(!bot) return command_result{error::vs_critical};
|
||||||
bot->set_bot_type(create_type);
|
bot->set_bot_type(create_type);
|
||||||
if(permissions[permission::i_client_music_create_modify_max_volume].has_value) {
|
if(permissions[permission::i_client_music_create_modify_max_volume].has_value) {
|
||||||
|
@ -169,21 +169,21 @@ command_result ConnectedClient::handleCommandMusicBotDelete(Command& cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
|
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
|
||||||
if(!bot) return command_result{error::music_invalid_id};
|
if(!bot) return command_result{error::music_invalid_id};
|
||||||
|
|
||||||
if(bot->getOwner() != this->getClientDatabaseId()) {
|
if(bot->getOwner() != this->getClientDatabaseId()) {
|
||||||
ACTION_REQUIRES_PERMISSION(permission::i_client_music_delete_power, bot->calculate_permission(permission::i_client_music_needed_delete_power, bot->getChannelId()), this->getChannelId());
|
ACTION_REQUIRES_PERMISSION(permission::i_client_music_delete_power, bot->calculate_permission(permission::i_client_music_needed_delete_power, bot->getChannelId()), this->getChannelId());
|
||||||
}
|
}
|
||||||
|
|
||||||
this->server->musicManager->deleteBot(bot);
|
this->server->music_manager_->deleteBot(bot);
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) {
|
command_result ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) {
|
||||||
if(!config::music::enabled) return command_result{error::music_disabled};
|
if(!config::music::enabled) return command_result{error::music_disabled};
|
||||||
|
|
||||||
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
|
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
|
||||||
if(!bot && cmd["bot_id"].as<ClientDbId>() != 0) return command_result{error::music_invalid_id};
|
if(!bot && cmd["bot_id"].as<ClientDbId>() != 0) return command_result{error::music_invalid_id};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -229,7 +229,7 @@ command_result ConnectedClient::handleCommandMusicBotPlayerInfo(Command& cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
|
|
||||||
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
|
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
|
||||||
if(!bot) return command_result{error::music_invalid_id};
|
if(!bot) return command_result{error::music_invalid_id};
|
||||||
|
|
||||||
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : "");
|
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : "");
|
||||||
|
@ -267,7 +267,7 @@ command_result ConnectedClient::handleCommandMusicBotPlayerAction(Command& cmd)
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
|
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
|
||||||
if(!bot) return command_result{error::music_invalid_id};
|
if(!bot) return command_result{error::music_invalid_id};
|
||||||
ACTION_REQUIRES_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, bot->getChannelId()), this->getChannelId());
|
ACTION_REQUIRES_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, bot->getChannelId()), this->getChannelId());
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ command_result ConnectedClient::handleCommandPlaylistList(ts::Command &cmd) {
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto self_ref = this->ref();
|
auto self_ref = this->ref();
|
||||||
auto playlists = this->server->musicManager->playlists();
|
auto playlists = this->server->music_manager_->playlists();
|
||||||
|
|
||||||
playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr<music::PlayablePlaylist>& playlist) {
|
playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr<music::PlayablePlaylist>& playlist) {
|
||||||
return playlist->client_has_permissions(self_ref, permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted) != permission::ok;
|
return playlist->client_has_permissions(self_ref, permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted) != permission::ok;
|
||||||
|
@ -354,13 +354,13 @@ command_result ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) {
|
||||||
{
|
{
|
||||||
auto max_playlists = this->calculate_permission(permission::i_max_playlists, 0);
|
auto max_playlists = this->calculate_permission(permission::i_max_playlists, 0);
|
||||||
if(max_playlists.has_value) {
|
if(max_playlists.has_value) {
|
||||||
auto playlists = ref_server->musicManager->find_playlists_by_client(this->getClientDatabaseId());
|
auto playlists = ref_server->music_manager_->find_playlists_by_client(this->getClientDatabaseId());
|
||||||
if(!permission::v2::permission_granted(playlists.size(), max_playlists))
|
if(!permission::v2::permission_granted(playlists.size(), max_playlists))
|
||||||
return command_result{permission::i_max_playlists};
|
return command_result{permission::i_max_playlists};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName());
|
auto playlist = ref_server->music_manager_->create_playlist(this->getClientDatabaseId(), this->getDisplayName());
|
||||||
if(!playlist) return command_result{error::vs_critical};
|
if(!playlist) return command_result{error::vs_critical};
|
||||||
|
|
||||||
playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL;
|
playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL;
|
||||||
|
@ -399,14 +399,14 @@ command_result ConnectedClient::handleCommandPlaylistDelete(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_delete_power, permission::i_playlist_delete_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_delete_power, permission::i_playlist_delete_power); perr)
|
||||||
return command_result{perr};
|
return command_result{perr};
|
||||||
|
|
||||||
string error;
|
string error;
|
||||||
if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) {
|
if(!ref_server->music_manager_->delete_playlist(playlist->playlist_id(), error)) {
|
||||||
logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error);
|
logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error);
|
||||||
return command_result{error::vs_critical};
|
return command_result{error::vs_critical};
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ command_result ConnectedClient::handleCommandPlaylistInfo(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted); perr)
|
||||||
|
@ -440,7 +440,7 @@ command_result ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_modify_power, permission::i_playlist_modify_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_modify_power, permission::i_playlist_modify_power); perr)
|
||||||
|
@ -498,7 +498,7 @@ command_result ConnectedClient::handleCommandPlaylistPermList(ts::Command &cmd)
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -551,7 +551,7 @@ command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
||||||
|
@ -581,7 +581,7 @@ command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
||||||
|
@ -611,7 +611,7 @@ command_result ConnectedClient::handleCommandPlaylistClientList(ts::Command &cmd
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -647,7 +647,7 @@ command_result ConnectedClient::handleCommandPlaylistClientPermList(ts::Command
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -710,7 +710,7 @@ command_result ConnectedClient::handleCommandPlaylistClientAddPerm(ts::Command &
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
|
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
|
||||||
|
@ -743,7 +743,7 @@ command_result ConnectedClient::handleCommandPlaylistClientDelPerm(ts::Command &
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
|
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
|
||||||
|
@ -815,7 +815,7 @@ command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd)
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
|
||||||
|
@ -860,7 +860,7 @@ command_result ConnectedClient::handleCommandPlaylistSongSetCurrent(ts::Command
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
|
||||||
|
@ -877,7 +877,7 @@ command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_add_power, permission::i_playlist_song_add_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_add_power, permission::i_playlist_song_add_power); perr)
|
||||||
|
@ -917,7 +917,7 @@ command_result ConnectedClient::handleCommandPlaylistSongReorder(ts::Command &cm
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
|
||||||
|
@ -940,7 +940,7 @@ command_result ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist) return command_result{error::playlist_invalid_id};
|
if(!playlist) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_remove_power, permission::i_playlist_song_remove_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_remove_power, permission::i_playlist_song_remove_power); perr)
|
||||||
|
@ -1135,21 +1135,21 @@ command_result ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command
|
||||||
CMD_RESET_IDLE;
|
CMD_RESET_IDLE;
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||||
|
|
||||||
auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]);
|
auto bot = ref_server->music_manager_->findBotById(cmd["bot_id"]);
|
||||||
if(!bot) return command_result{error::music_invalid_id};
|
if(!bot) return command_result{error::music_invalid_id};
|
||||||
if(bot->getOwner() != this->getClientDatabaseId())
|
if(bot->getOwner() != this->getClientDatabaseId())
|
||||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, 0));
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, 0));
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
|
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
if(ref_server->musicManager->find_bot_by_playlist(playlist))
|
if(ref_server->music_manager_->find_bot_by_playlist(playlist))
|
||||||
return command_result{error::playlist_already_in_use};
|
return command_result{error::playlist_already_in_use};
|
||||||
|
|
||||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
|
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
|
||||||
return command_result{perr};
|
return command_result{perr};
|
||||||
|
|
||||||
if(!ref_server->musicManager->assign_playlist(bot, playlist))
|
if(!ref_server->music_manager_->assign_playlist(bot, playlist))
|
||||||
return command_result{error::vs_critical};
|
return command_result{error::vs_critical};
|
||||||
|
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
|
@ -1162,7 +1162,7 @@ command_result ConnectedClient::handleCommandPlaylistSetSubscription(ts::Command
|
||||||
|
|
||||||
if(!config::music::enabled) return command_result{error::music_disabled};
|
if(!config::music::enabled) return command_result{error::music_disabled};
|
||||||
|
|
||||||
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
|
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
|
||||||
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
|
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -293,7 +293,7 @@ command_result ConnectedClient::handleCommandServerGroupList(Command &) {
|
||||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_list, 1);
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_list, 1);
|
||||||
|
|
||||||
this->notifyServerGroupList();
|
this->notifyServerGroupList(this->getType() != ClientType::CLIENT_QUERY);
|
||||||
this->command_times.servergrouplist = system_clock::now();
|
this->command_times.servergrouplist = system_clock::now();
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
}
|
}
|
||||||
|
|
|
@ -416,8 +416,8 @@ command_result QueryClient::handleCommandServerInfo(Command &) {
|
||||||
|
|
||||||
if(this->server && permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_connectioninfo_view, 0))) {
|
if(this->server && permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_connectioninfo_view, 0))) {
|
||||||
auto total_stats = this->server->getServerStatistics()->total_stats();
|
auto total_stats = this->server->getServerStatistics()->total_stats();
|
||||||
auto report_second = this->server->serverStatistics->second_stats();
|
auto report_second = this->server->server_statistics_->second_stats();
|
||||||
auto report_minute = this->server->serverStatistics->minute_stats();
|
auto report_minute = this->server->server_statistics_->minute_stats();
|
||||||
cmd["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), (size_t) 0U);
|
cmd["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), (size_t) 0U);
|
||||||
cmd["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), (size_t) 0U);
|
cmd["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), (size_t) 0U);
|
||||||
cmd["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), (size_t) 0U);
|
cmd["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), (size_t) 0U);
|
||||||
|
|
|
@ -118,8 +118,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co
|
||||||
this->connection->packet_statistics().reset_offsets();
|
this->connection->packet_statistics().reset_offsets();
|
||||||
|
|
||||||
bool use_teaspeak = cmd.has_switch("teaspeak");
|
bool use_teaspeak = cmd.has_switch("teaspeak");
|
||||||
if(use_teaspeak ? !config::server::clients::teaspeak : !config::server::clients::teamspeak)
|
if(!use_teaspeak && !config::server::clients::teamspeak) {
|
||||||
return command_result{error::client_type_is_not_allowed};
|
return command_result{error::client_type_is_not_allowed, config::server::clients::teamspeak_not_allowed_message};
|
||||||
|
}
|
||||||
|
|
||||||
if(use_teaspeak) {
|
if(use_teaspeak) {
|
||||||
debugMessage(this->connection->virtual_server_id(), "{} Client using TeaSpeak.", this->connection->log_prefix());
|
debugMessage(this->connection->virtual_server_id(), "{} Client using TeaSpeak.", this->connection->log_prefix());
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
#include <log/LogUtils.h>
|
#include <log/LogUtils.h>
|
||||||
|
|
||||||
#include "VoiceClient.h"
|
#include "VoiceClient.h"
|
||||||
#include "src/VirtualServer.h"
|
|
||||||
#include "../../server/VoiceServer.h"
|
|
||||||
#include "src/InstanceHandler.h"
|
#include "src/InstanceHandler.h"
|
||||||
#include "src/manager/ActionLogger.h"
|
#include "src/manager/ActionLogger.h"
|
||||||
|
|
||||||
|
@ -277,4 +275,12 @@ float VoiceClient::current_ping_deviation() {
|
||||||
|
|
||||||
float VoiceClient::current_packet_loss() const {
|
float VoiceClient::current_packet_loss() const {
|
||||||
return this->connection->packet_statistics().current_packet_loss();
|
return this->connection->packet_statistics().current_packet_loss();
|
||||||
}
|
}
|
||||||
|
void VoiceClient::processJoin() {
|
||||||
|
SpeakingClient::processJoin();
|
||||||
|
if(this->rtc_client_id > 0) {
|
||||||
|
auto sender = this->server->rtc_server().create_audio_source_supplier_sender(this->rtc_client_id);
|
||||||
|
assert(sender.has_value());
|
||||||
|
this->rtc_audio_supplier.reset(*sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -96,12 +96,17 @@ namespace ts {
|
||||||
const VoicePacketFlags &flags
|
const VoicePacketFlags &flags
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
|
void processJoin() override;
|
||||||
protected:
|
protected:
|
||||||
virtual command_result handleCommand(Command &command) override;
|
virtual command_result handleCommand(Command &command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void finalDisconnect();
|
void finalDisconnect();
|
||||||
bool final_disconnected = false;
|
bool final_disconnected = false;
|
||||||
|
|
||||||
|
rtc::NativeAudioSourceSupplier rtc_audio_supplier{};
|
||||||
|
uint16_t stop_seq_counter{0};
|
||||||
|
|
||||||
//General TS3 manager commands
|
//General TS3 manager commands
|
||||||
command_result handleCommandClientInit(Command&) override;
|
command_result handleCommandClientInit(Command&) override;
|
||||||
command_result handleCommandClientDisconnect(Command&);
|
command_result handleCommandClientDisconnect(Command&);
|
||||||
|
|
|
@ -220,6 +220,10 @@ void VoiceClientConnection::send_packet(protocol::PacketType type, protocol::Pac
|
||||||
this->packet_encoder_.send_packet(type, flag, payload, payload_size);
|
this->packet_encoder_.send_packet(type, flag, payload, payload_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoiceClientConnection::send_packet(protocol::OutgoingServerPacket* packet) {
|
||||||
|
this->packet_encoder_.send_packet(packet);
|
||||||
|
}
|
||||||
|
|
||||||
void VoiceClientConnection::send_command(const std::string_view &cmd, bool b, std::unique_ptr<threads::Future<bool>> cb) {
|
void VoiceClientConnection::send_command(const std::string_view &cmd, bool b, std::unique_ptr<threads::Future<bool>> cb) {
|
||||||
this->packet_encoder_.send_command(cmd, b, std::move(cb));
|
this->packet_encoder_.send_command(cmd, b, std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ namespace ts {
|
||||||
virtual ~VoiceClientConnection();
|
virtual ~VoiceClientConnection();
|
||||||
|
|
||||||
void send_packet(protocol::PacketType /* type */, protocol::PacketFlag::PacketFlags /* flags */, const void* /* payload */, size_t /* payload length */);
|
void send_packet(protocol::PacketType /* type */, protocol::PacketFlag::PacketFlags /* flags */, const void* /* payload */, size_t /* payload length */);
|
||||||
|
void send_packet(protocol::OutgoingServerPacket* /* packet */); /* method takes ownership of the packet */
|
||||||
void send_command(const std::string_view& /* build command command */, bool /* command low */, std::unique_ptr<threads::Future<bool>> /* acknowledge listener */);
|
void send_command(const std::string_view& /* build command command */, bool /* command low */, std::unique_ptr<threads::Future<bool>> /* acknowledge listener */);
|
||||||
|
|
||||||
CryptHandler* getCryptHandler(){ return &crypt_handler; }
|
CryptHandler* getCryptHandler(){ return &crypt_handler; }
|
||||||
|
|
|
@ -28,7 +28,15 @@ void VoiceClientConnection::handlePacketVoice(const protocol::ClientPacketParser
|
||||||
auto client = this->getCurrentClient();
|
auto client = this->getCurrentClient();
|
||||||
if(!client) return;
|
if(!client) return;
|
||||||
|
|
||||||
client->handlePacketVoice(packet.payload(), (packet.flags() & PacketFlag::Compressed) > 0, (packet.flags() & PacketFlag::Fragmented) > 0);
|
if(client->should_handle_voice_packet(packet.payload_length())) {
|
||||||
|
auto& sink = client->rtc_audio_supplier;
|
||||||
|
|
||||||
|
auto payload = packet.payload();
|
||||||
|
uint16_t vpacketId = be2le16((char*) payload.data_ptr());
|
||||||
|
auto codec = (uint8_t) payload[2];
|
||||||
|
|
||||||
|
sink.send_audio(vpacketId, false, vpacketId * 960, codec, std::string_view{payload.data_ptr<char>() + 3, payload.length() - 3});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {
|
void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
#include <cstring>
|
|
||||||
#include "SampleHandler.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace ts;
|
|
||||||
using namespace ts::sound;
|
|
||||||
|
|
||||||
SampleConverter::SampleConverter(size_t channelCount, size_t segmentSize) : channelCount(channelCount), segmentSize(segmentSize) { }
|
|
||||||
SampleConverter::~SampleConverter() = default;
|
|
||||||
|
|
||||||
void SampleConverter::pushSamples(float* pcm, size_t size) {
|
|
||||||
threads::MutexLock l(this->segmentLock);
|
|
||||||
|
|
||||||
size_t pcmIndex = 0;
|
|
||||||
if(!this->segments.empty()) {
|
|
||||||
if(!this->segments.back()->full) {
|
|
||||||
auto& last = this->segments.back();
|
|
||||||
if(last->sampleSize < this->segmentSize) { //Enought space
|
|
||||||
size_t pcmWrites = min(this->segmentSize - last->sampleSize, size);
|
|
||||||
|
|
||||||
auto buffer = static_cast<float *>(malloc(this->segmentSize * this->channelCount * sizeof(float)));
|
|
||||||
memcpy(buffer, last->samples, last->channelCount * last->sampleSize * sizeof(float));
|
|
||||||
free(last->samples);
|
|
||||||
|
|
||||||
memcpy((void*) &buffer[last->channelCount * last->sampleSize], pcm, pcmWrites * this->channelCount * sizeof(float));
|
|
||||||
pcmIndex += this->channelCount * pcmWrites;
|
|
||||||
|
|
||||||
last->sampleSize += pcmWrites * this->channelCount;
|
|
||||||
last->samples = buffer;
|
|
||||||
last->full = last->sampleSize == this->segmentSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(size - (pcmIndex / this->channelCount) > this->segmentSize) {
|
|
||||||
auto segment = make_shared<SampleSegment>();
|
|
||||||
segment->channelCount = this->channelCount;
|
|
||||||
segment->sampleSize = this->segmentSize;
|
|
||||||
segment->full = true;
|
|
||||||
|
|
||||||
auto bufLen = this->segmentSize * this->channelCount * sizeof(float);
|
|
||||||
segment->samples = static_cast<float *>(malloc(bufLen));
|
|
||||||
memcpy(segment->samples, &pcm[pcmIndex], bufLen);
|
|
||||||
pcmIndex += bufLen / sizeof(float);
|
|
||||||
|
|
||||||
this->segments.push_back(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto overhead = size - (pcmIndex / this->channelCount);
|
|
||||||
if(overhead > 0){
|
|
||||||
auto segment = make_shared<SampleSegment>();
|
|
||||||
segment->channelCount = this->channelCount;
|
|
||||||
segment->sampleSize = overhead;
|
|
||||||
segment->full = false;
|
|
||||||
|
|
||||||
auto bufLen = overhead * this->channelCount * sizeof(float);
|
|
||||||
segment->samples = static_cast<float *>(malloc(bufLen));
|
|
||||||
memcpy(segment->samples, &pcm[pcmIndex], bufLen);
|
|
||||||
this->segments.push_back(segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SampleSegment> SampleConverter::nextSegment() {
|
|
||||||
threads::MutexLock l(this->segmentLock);
|
|
||||||
if(this->segments.empty()) return nullptr;
|
|
||||||
if(!this->segments.front()->full && this->segments.size() == 1) return nullptr;
|
|
||||||
|
|
||||||
auto elm = std::move(this->segments.front());
|
|
||||||
this->segments.pop_front();
|
|
||||||
return elm;
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <deque>
|
|
||||||
#include <memory>
|
|
||||||
#include <ThreadPool/Mutex.h>
|
|
||||||
|
|
||||||
namespace ts {
|
|
||||||
namespace sound {
|
|
||||||
struct SampleSegment {
|
|
||||||
float* samples;
|
|
||||||
size_t channelCount;
|
|
||||||
size_t sampleSize;
|
|
||||||
bool full = false;
|
|
||||||
|
|
||||||
SampleSegment(){}
|
|
||||||
~SampleSegment(){
|
|
||||||
if(samples) free(samples);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SampleConverter {
|
|
||||||
public:
|
|
||||||
SampleConverter(size_t channelCount, size_t segmentSize);
|
|
||||||
~SampleConverter();
|
|
||||||
|
|
||||||
void pushSamples(float* pcm, size_t size);
|
|
||||||
std::shared_ptr<SampleSegment> nextSegment();
|
|
||||||
inline bool hasNext(){
|
|
||||||
threads::MutexLock l(this->segmentLock);
|
|
||||||
return !this->segments.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t getSegmentSize() { return this->segmentSize; }
|
|
||||||
private:
|
|
||||||
size_t channelCount = 1;
|
|
||||||
size_t segmentSize = 0;
|
|
||||||
threads::Mutex segmentLock;
|
|
||||||
std::deque<std::shared_ptr<SampleSegment>> segments;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,303 +0,0 @@
|
||||||
#include <misc/std_unique_ptr.h>
|
|
||||||
#include <log/LogUtils.h>
|
|
||||||
#include <misc/endianness.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include "WebClient.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace ts;
|
|
||||||
using namespace ts::server;
|
|
||||||
using namespace ts::web;
|
|
||||||
|
|
||||||
void VoiceBridge::callback_log(void* ptr, pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...) {
|
|
||||||
auto max_length = 1024 * 8;
|
|
||||||
char buffer[max_length];
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, message);
|
|
||||||
max_length = vsnprintf(buffer, max_length, message.c_str(), args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
auto bridge = (VoiceBridge*) ptr;
|
|
||||||
debugMessage(LOG_GENERAL, "{}[WebRTC][{}][{}] {}", CLIENT_STR_LOG_PREFIX_(bridge->owner()), level, name, string(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace gioloop {
|
|
||||||
void* main_loop_;
|
|
||||||
|
|
||||||
void*(*g_main_loop_new)(void* /* context */, bool /* is true */);
|
|
||||||
void(*g_main_loop_run)(void* /* loop */);
|
|
||||||
void(*g_main_loop_unref)(void* /* loop */);
|
|
||||||
void*(*g_main_loop_ref)(void* /* loop */);
|
|
||||||
|
|
||||||
bool initialized{false};
|
|
||||||
void initialize() {
|
|
||||||
if(initialized) return;
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
g_main_loop_new = (decltype(g_main_loop_new)) dlsym(nullptr, "g_main_loop_new");
|
|
||||||
g_main_loop_run = (decltype(g_main_loop_run)) dlsym(nullptr, "g_main_loop_run");
|
|
||||||
g_main_loop_ref = (decltype(g_main_loop_ref)) dlsym(nullptr, "g_main_loop_ref");
|
|
||||||
g_main_loop_unref = (decltype(g_main_loop_unref)) dlsym(nullptr, "g_main_loop_unref");
|
|
||||||
|
|
||||||
if(!g_main_loop_run || !g_main_loop_new || !g_main_loop_ref || !g_main_loop_unref) {
|
|
||||||
logWarning(LOG_INSTANCE, "Missing g_main_loop_new, g_main_loop_run, g_main_loop_ref or g_main_loop_unref functions. Could not spawn main loop.");
|
|
||||||
g_main_loop_run = nullptr;
|
|
||||||
g_main_loop_new = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
main_loop_ = g_main_loop_new(nullptr, false);
|
|
||||||
if(!main_loop_) {
|
|
||||||
logError(LOG_INSTANCE, "Failed to spawn new event loop for the web client.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread([]{
|
|
||||||
g_main_loop_run(main_loop_);
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<GMainLoop> loop() {
|
|
||||||
return std::shared_ptr<GMainLoop>{(GMainLoop*) g_main_loop_ref(main_loop_), g_main_loop_unref};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceBridge::VoiceBridge(const shared_ptr<WebClient>& owner) : _owner(owner) {
|
|
||||||
auto config = make_shared<rtc::PeerConnection::Config>();
|
|
||||||
config->nice_config = make_shared<rtc::NiceWrapper::Config>();
|
|
||||||
|
|
||||||
config->nice_config->ice_port_range = {config::web::webrtc_port_min, config::web::webrtc_port_max};
|
|
||||||
if(config::web::stun_enabled)
|
|
||||||
config->nice_config->stun_server = { config::web::stun_host, config::web::stun_port };
|
|
||||||
|
|
||||||
config->nice_config->allow_ice_udp = config::web::udp_enabled;
|
|
||||||
config->nice_config->allow_ice_tcp = config::web::tcp_enabled;
|
|
||||||
config->nice_config->use_upnp = config::web::enable_upnp;
|
|
||||||
|
|
||||||
gioloop::initialize();
|
|
||||||
config->event_loop = gioloop::loop();
|
|
||||||
/*
|
|
||||||
config->nice_config->main_loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, false), g_main_loop_unref);
|
|
||||||
std::thread(g_main_loop_run, config->nice_config->main_loop.get()).detach();
|
|
||||||
*/
|
|
||||||
|
|
||||||
config->logger = make_shared<pipes::Logger>();
|
|
||||||
config->logger->callback_log = VoiceBridge::callback_log;
|
|
||||||
config->logger->callback_argument = this;
|
|
||||||
//config->sctp.local_port = 5202; //Fire Fox don't support a different port :D
|
|
||||||
|
|
||||||
this->connection = make_unique<rtc::PeerConnection>(config) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceBridge::~VoiceBridge() {
|
|
||||||
__asm__("nop");
|
|
||||||
}
|
|
||||||
|
|
||||||
int VoiceBridge::server_id() {
|
|
||||||
auto locked = this->_owner.lock();
|
|
||||||
return locked ? locked->getServerId() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<server::WebClient> VoiceBridge::owner() {
|
|
||||||
return this->_owner.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VoiceBridge::initialize(std::string &error) {
|
|
||||||
if(!this->connection->initialize(error)) return false;
|
|
||||||
|
|
||||||
this->connection->callback_ice_candidate = [&](const rtc::IceCandidate& candidate) {
|
|
||||||
if(!candidate.is_finished_candidate()) {
|
|
||||||
if(auto callback{this->callback_ice_candidate}; callback)
|
|
||||||
callback(candidate);
|
|
||||||
} else {
|
|
||||||
if(auto callback{this->callback_ice_candidate_finished}; callback)
|
|
||||||
callback(candidate.sdpMid, candidate.sdpMLineIndex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this->connection->callback_new_stream = [&](const std::shared_ptr<rtc::Channel> &channel) { this->handle_media_stream(channel); }; //bind(&VoiceBridge::handle_media_stream, this, placeholders::_1); => crash
|
|
||||||
this->connection->callback_setup_fail = [&](rtc::PeerConnection::ConnectionComponent comp, const std::string& reason) {
|
|
||||||
debugMessage(this->server_id(), "{} WebRTC setup failed! Component {} ({})", CLIENT_STR_LOG_PREFIX_(this->owner()), comp, reason);
|
|
||||||
if(this->callback_failed)
|
|
||||||
this->callback_failed();
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VoiceBridge::parse_offer(const std::string &sdp) {
|
|
||||||
this->offer_timestamp = chrono::system_clock::now();
|
|
||||||
string error;
|
|
||||||
return this->connection->apply_offer(error, sdp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int VoiceBridge::apply_ice(const std::deque<std::shared_ptr<rtc::IceCandidate>>& candidates) {
|
|
||||||
return this->connection->apply_ice_candidates(candidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::remote_ice_finished() {
|
|
||||||
this->connection->remote_candidates_finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VoiceBridge::generate_answer() {
|
|
||||||
return this->connection->generate_answer(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::execute_tick() {
|
|
||||||
if(!this->voice_channel_) {
|
|
||||||
if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds{20} < chrono::system_clock::now()) {
|
|
||||||
this->offer_timestamp = chrono::system_clock::time_point();
|
|
||||||
this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::handle_media_stream(const std::shared_ptr<rtc::Channel> &undefined_stream) {
|
|
||||||
if(undefined_stream->type() == rtc::CHANTYPE_APPLICATION) {
|
|
||||||
auto stream = dynamic_pointer_cast<rtc::ApplicationChannel>(undefined_stream);
|
|
||||||
if(!stream) return;
|
|
||||||
|
|
||||||
stream->callback_datachannel_new = [&](const std::shared_ptr<rtc::DataChannel> &channel) { this->handle_data_channel(channel); }; //bind(&VoiceBridge::handle_data_channel, this, placeholders::_1); => may crash?
|
|
||||||
} else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) {
|
|
||||||
auto stream = dynamic_pointer_cast<rtc::AudioChannel>(undefined_stream);
|
|
||||||
if(!stream) return;
|
|
||||||
|
|
||||||
logTrace(this->server_id(), "Audio channel extensions:");
|
|
||||||
for(const auto& ex : stream->list_extensions()) {
|
|
||||||
logTrace(this->server_id(), " - {}: {}", ex->id, ex->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->register_local_extension("urn:ietf:params:rtp-hdrext:ssrc-audio-level");
|
|
||||||
for(const auto& codec : stream->list_codecs()) {
|
|
||||||
if(codec->type == rtc::codec::Codec::OPUS) {
|
|
||||||
codec->accepted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!this->incoming_voice_channel_.lock()) {
|
|
||||||
debugMessage(this->server_id(), "Having client's voice audio stream.");
|
|
||||||
this->incoming_voice_channel_ = stream;
|
|
||||||
stream->incoming_data_handler = [&](const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
|
||||||
this->handle_audio_voice_data(channel, data, payload_offset); };
|
|
||||||
} else if(!this->incoming_whisper_channel_.lock()) {
|
|
||||||
debugMessage(this->server_id(), "Having client's whispers audio stream.");
|
|
||||||
this->incoming_whisper_channel_ = stream;
|
|
||||||
stream->incoming_data_handler = [&](const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
|
||||||
this->handle_audio_voice_whisper_data(channel, data, payload_offset); };
|
|
||||||
} else {
|
|
||||||
debugMessage(this->server_id(), "Client sdp offer contains more than two voice channels.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logError(this->server_id(), "Got offer for unknown channel of type {}", undefined_stream->type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) {
|
|
||||||
if(channel->lable() == "main" || channel->lable() == "voice") {
|
|
||||||
this->voice_channel_ = channel;
|
|
||||||
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
|
||||||
this->callback_initialized();
|
|
||||||
|
|
||||||
weak_ptr<rtc::DataChannel> weak_channel = channel;
|
|
||||||
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
|
|
||||||
if(buffer.length() < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this->callback_voice_data(buffer.view(2), buffer[0] == 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
channel->callback_close = [&, weak_channel] {
|
|
||||||
auto channel_ref = weak_channel.lock();
|
|
||||||
if(channel_ref == this->voice_channel_) {
|
|
||||||
this->voice_channel_ = nullptr;
|
|
||||||
//TODO may callback?
|
|
||||||
debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if(channel->lable() == "voice-whisper") {
|
|
||||||
this->voice_whisper_channel_ = channel;
|
|
||||||
debugMessage(this->server_id(), "{} Got voice whisper channel", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
|
||||||
|
|
||||||
weak_ptr<rtc::DataChannel> weak_channel = channel;
|
|
||||||
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
|
|
||||||
if(buffer.length() < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this->callback_voice_whisper_data(buffer.view(1), buffer[0] == 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
channel->callback_close = [&, weak_channel] {
|
|
||||||
auto channel_ref = weak_channel.lock();
|
|
||||||
if(channel_ref == this->voice_whisper_channel_) {
|
|
||||||
this->voice_whisper_channel_ = nullptr;
|
|
||||||
debugMessage(this->server_id(), "{} Voice whisper channel has been closed.", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::handle_audio_voice_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
|
||||||
if(channel->codec->type != rtc::codec::Codec::OPUS) {
|
|
||||||
//debugMessage(this->server_id(), "{} Got unknown codec ({})!", CLIENT_STR_LOG_PREFIX_(this->owner()), channel->codec->type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->handle_audio_voice_x_data(&this->voice_state, data, payload_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::handle_audio_voice_whisper_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
|
||||||
if(channel->codec->type != rtc::codec::Codec::OPUS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->handle_audio_voice_x_data(&this->whisper_state, data, payload_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoiceBridge::handle_audio_voice_x_data(VoiceStateData *state, const pipes::buffer_view &data, size_t payload_offset) {
|
|
||||||
bool is_silence{false};
|
|
||||||
|
|
||||||
auto audio_channel = state->channel.lock();
|
|
||||||
if(!audio_channel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const auto& ext : audio_channel->list_extensions(rtc::direction::incoming)) {
|
|
||||||
if(ext->name == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") {
|
|
||||||
int level;
|
|
||||||
if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) {
|
|
||||||
//debugMessage(this->server_id(), "Audio level: {}", level);
|
|
||||||
if(level == 127) {
|
|
||||||
is_silence = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_silence) {
|
|
||||||
if(state->muted) {
|
|
||||||
/* the muted state is already set */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state->muted = true;
|
|
||||||
|
|
||||||
auto target_buffer = buffer::allocate_buffer(3);
|
|
||||||
le2be16(state->sequence_packet_id++, (char*) target_buffer.data_ptr());
|
|
||||||
target_buffer[2] = 5;
|
|
||||||
|
|
||||||
state->callback(target_buffer, false);
|
|
||||||
} else {
|
|
||||||
if(state->muted) {
|
|
||||||
state->muted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3);
|
|
||||||
le2be16(state->sequence_packet_id++, (char*) target_buffer.data_ptr());
|
|
||||||
target_buffer[2] = 5;
|
|
||||||
memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset);
|
|
||||||
|
|
||||||
state->callback(target_buffer, state->sequence_packet_id < 7);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <pipes/rtc/PeerConnection.h>
|
|
||||||
#include <pipes/rtc/channels/ApplicationChannel.h>
|
|
||||||
#include <pipes/rtc/channels/AudioChannel.h>
|
|
||||||
|
|
||||||
namespace ts {
|
|
||||||
namespace server {
|
|
||||||
class WebClient;
|
|
||||||
}
|
|
||||||
namespace web {
|
|
||||||
class VoiceBridge {
|
|
||||||
public:
|
|
||||||
typedef std::function<void(const pipes::buffer_view&, bool /* is sequence start */)> cb_voice_data;
|
|
||||||
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
|
|
||||||
typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
|
|
||||||
typedef std::function<void()> cb_initialized;
|
|
||||||
typedef std::function<void()> cb_failed;
|
|
||||||
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_channel() { return this->voice_channel_; }
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_whisper_channel() { return this->voice_whisper_channel_; }
|
|
||||||
|
|
||||||
explicit VoiceBridge(const std::shared_ptr<server::WebClient>&);
|
|
||||||
virtual ~VoiceBridge();
|
|
||||||
|
|
||||||
bool initialize(std::string&);
|
|
||||||
bool parse_offer(const std::string&);
|
|
||||||
int apply_ice(const std::deque<std::shared_ptr<rtc::IceCandidate>>& /* candidates */);
|
|
||||||
void remote_ice_finished();
|
|
||||||
std::string generate_answer();
|
|
||||||
|
|
||||||
cb_ice_candidate callback_ice_candidate;
|
|
||||||
cb_ice_candidate_finish callback_ice_candidate_finished;
|
|
||||||
cb_voice_data callback_voice_data;
|
|
||||||
cb_voice_data callback_voice_whisper_data;
|
|
||||||
cb_initialized callback_initialized;
|
|
||||||
cb_failed callback_failed;
|
|
||||||
|
|
||||||
void execute_tick();
|
|
||||||
private:
|
|
||||||
struct VoiceStateData {
|
|
||||||
uint16_t sequence_packet_id{0};
|
|
||||||
bool muted{true};
|
|
||||||
|
|
||||||
std::weak_ptr<rtc::AudioChannel>& channel;
|
|
||||||
cb_voice_data& callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void callback_log(void* ptr, pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...);
|
|
||||||
|
|
||||||
inline int server_id();
|
|
||||||
inline std::shared_ptr<server::WebClient> owner();
|
|
||||||
|
|
||||||
void handle_media_stream(const std::shared_ptr<rtc::Channel>& /* stream */);
|
|
||||||
void handle_data_channel(const std::shared_ptr<rtc::DataChannel> & /* channel */);
|
|
||||||
|
|
||||||
void handle_audio_voice_data(const std::shared_ptr<rtc::MediaChannel>& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
|
|
||||||
void handle_audio_voice_whisper_data(const std::shared_ptr<rtc::MediaChannel>& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
|
|
||||||
|
|
||||||
static void handle_audio_voice_x_data(VoiceStateData* /* state */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
|
|
||||||
|
|
||||||
std::weak_ptr<server::WebClient> _owner;
|
|
||||||
std::chrono::system_clock::time_point offer_timestamp;
|
|
||||||
std::unique_ptr<rtc::PeerConnection> connection;
|
|
||||||
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_channel_{};
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_{};
|
|
||||||
|
|
||||||
std::weak_ptr<rtc::AudioChannel> incoming_voice_channel_{};
|
|
||||||
std::weak_ptr<rtc::AudioChannel> incoming_whisper_channel_{};
|
|
||||||
|
|
||||||
VoiceStateData voice_state{
|
|
||||||
.channel = this->incoming_voice_channel_,
|
|
||||||
.callback = this->callback_voice_data
|
|
||||||
};
|
|
||||||
VoiceStateData whisper_state{
|
|
||||||
.channel = this->incoming_whisper_channel_,
|
|
||||||
.callback = this->callback_voice_whisper_data
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
|
||||||
buffer_lock.unlock();
|
buffer_lock.unlock();
|
||||||
|
|
||||||
if (errno == EINTR || errno == EAGAIN) {
|
if (errno == EINTR || errno == EAGAIN) {
|
||||||
lock_guard event_lock(this->event_lock);
|
lock_guard event_lock(this->event_mutex);
|
||||||
if(this->writeEvent)
|
if(this->writeEvent)
|
||||||
event_add(this->writeEvent, nullptr);
|
event_add(this->writeEvent, nullptr);
|
||||||
return;
|
return;
|
||||||
|
@ -31,10 +31,12 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
|
||||||
//new ServerConnection(globalClient).startConnection({ host: "localhost", port: 9987}, new HandshakeHandler(profiles.default_profile(), "test"))
|
//new ServerConnection(globalClient).startConnection({ host: "localhost", port: 9987}, new HandshakeHandler(profiles.default_profile(), "test"))
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard event_lock(this->event_lock);
|
std::lock_guard event_lock{this->event_mutex};
|
||||||
event_del_noblock(this->writeEvent);
|
if(this->writeEvent) {
|
||||||
event_free(this->writeEvent);
|
event_del_noblock(this->writeEvent);
|
||||||
this->writeEvent = nullptr;
|
event_free(this->writeEvent);
|
||||||
|
this->writeEvent = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
debugMessage(this->getServerId(), "[{}] Failed to write message (length {}, errno {}, message {}) Disconnecting client.", CLIENT_STR_LOG_PREFIX, written, errno, strerror(errno));
|
debugMessage(this->getServerId(), "[{}] Failed to write message (length {}, errno {}, message {}) Disconnecting client.", CLIENT_STR_LOG_PREFIX, written, errno, strerror(errno));
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
|
||||||
|
|
||||||
/* reschedule new write */
|
/* reschedule new write */
|
||||||
buffer_lock.unlock();
|
buffer_lock.unlock();
|
||||||
lock_guard event_lock(this->event_lock);
|
lock_guard event_lock(this->event_mutex);
|
||||||
if(this->writeEvent)
|
if(this->writeEvent)
|
||||||
event_add(this->writeEvent, nullptr);
|
event_add(this->writeEvent, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +74,7 @@ void WebClient::handleMessageRead(int fd, short, void *) {
|
||||||
debugMessage(this->getServerId(), "[{}] Failed to read message (length {}, errno {}, message: {}). Closing connection.", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno));
|
debugMessage(this->getServerId(), "[{}] Failed to read message (length {}, errno {}, message: {}). Closing connection.", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno));
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard lock(this->event_lock);
|
lock_guard lock(this->event_mutex);
|
||||||
if(this->readEvent)
|
if(this->readEvent)
|
||||||
event_del_noblock(this->readEvent);
|
event_del_noblock(this->readEvent);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +101,7 @@ void WebClient::enqueue_raw_packet(const pipes::buffer_view &msg) {
|
||||||
this->queue_write.push_back(buffer);
|
this->queue_write.push_back(buffer);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
lock_guard lock(this->event_lock);
|
lock_guard lock(this->event_mutex);
|
||||||
if(this->writeEvent)
|
if(this->writeEvent)
|
||||||
event_add(this->writeEvent, nullptr);
|
event_add(this->writeEvent, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,11 +275,7 @@ command_result WebClient::handleCommand(Command &command) {
|
||||||
|
|
||||||
void WebClient::tick(const std::chrono::system_clock::time_point& point) {
|
void WebClient::tick(const std::chrono::system_clock::time_point& point) {
|
||||||
SpeakingClient::tick(point);
|
SpeakingClient::tick(point);
|
||||||
{
|
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
|
||||||
if(this->voice_bridge)
|
|
||||||
this->voice_bridge->execute_tick();
|
|
||||||
}
|
|
||||||
if(this->ping.last_request + seconds(1) < point) {
|
if(this->ping.last_request + seconds(1) < point) {
|
||||||
if(this->ping.last_response > this->ping.last_request || this->ping.last_response + this->ping.timeout < point) {
|
if(this->ping.last_response > this->ping.last_request || this->ping.last_response + this->ping.timeout < point) {
|
||||||
this->ping.current_id++;
|
this->ping.current_id++;
|
||||||
|
@ -390,7 +386,7 @@ void WebClient::disconnectFinal() {
|
||||||
{
|
{
|
||||||
::event *event_read, *event_write;
|
::event *event_read, *event_write;
|
||||||
{
|
{
|
||||||
unique_lock event_lock(this->event_lock);
|
unique_lock event_lock(this->event_mutex);
|
||||||
|
|
||||||
event_read = this->readEvent;
|
event_read = this->readEvent;
|
||||||
event_write = this->writeEvent;
|
event_write = this->writeEvent;
|
||||||
|
@ -417,14 +413,6 @@ void WebClient::disconnectFinal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->processLeave();
|
this->processLeave();
|
||||||
{
|
|
||||||
|
|
||||||
unique_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
|
||||||
if(this->voice_bridge) {
|
|
||||||
//TODO correct close?
|
|
||||||
this->voice_bridge = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->ssl_handler.finalize();
|
this->ssl_handler.finalize();
|
||||||
this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
|
this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
|
||||||
|
@ -470,179 +458,6 @@ void WebClient::handleMessage(const pipes::buffer_view &message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->handleCommandFull(cmd, true);
|
this->handleCommandFull(cmd, true);
|
||||||
} else if(val["type"].asString() == "WebRTC") {
|
|
||||||
auto subType = val["request"].asString();
|
|
||||||
if(subType == "create") {
|
|
||||||
std::unique_lock voice_bridge_lock_{this->voice_bridge_lock};
|
|
||||||
if(this->voice_bridge) {
|
|
||||||
logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this));
|
|
||||||
|
|
||||||
std::thread([&, vb_ptr = std::move(this->voice_bridge), lock = this->ref()]() mutable {
|
|
||||||
vb_ptr = nullptr;
|
|
||||||
lock = nullptr;
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(this->ref())); //FIXME Add config
|
|
||||||
|
|
||||||
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool head) {
|
|
||||||
/* may somehow get the "real" packet size? */
|
|
||||||
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
|
||||||
this->handlePacketVoice(buffer, head, false);
|
|
||||||
};
|
|
||||||
this->voice_bridge->callback_voice_whisper_data = [&](const pipes::buffer_view& buffer, bool head) {
|
|
||||||
/* may somehow get the "real" packet size? */
|
|
||||||
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
|
||||||
|
|
||||||
constexpr static auto kTempBufferSize{2048};
|
|
||||||
char temp_buffer[kTempBufferSize];
|
|
||||||
size_t offset{0};
|
|
||||||
|
|
||||||
/* copy the voice header */
|
|
||||||
memcpy(temp_buffer, buffer.data_ptr(), 3);
|
|
||||||
offset += 3;
|
|
||||||
|
|
||||||
bool is_new;
|
|
||||||
{
|
|
||||||
std::lock_guard whisper_header_lock{this->whisper.mutex};
|
|
||||||
if(!this->whisper.is_set) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(temp_buffer + offset, this->whisper.target_header.data_ptr(), this->whisper.target_header.length());
|
|
||||||
offset += this->whisper.target_header.length();
|
|
||||||
|
|
||||||
is_new = this->whisper.is_new_header;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(temp_buffer + offset, buffer.data_ptr<char>() + 3, buffer.length() - 3);
|
|
||||||
offset += buffer.length() - 3;
|
|
||||||
|
|
||||||
this->handlePacketVoiceWhisper(pipes::buffer_view{temp_buffer, offset}, is_new, head);
|
|
||||||
};
|
|
||||||
this->voice_bridge->callback_initialized = [&](){
|
|
||||||
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->voice_bridge->callback_failed = [&] {
|
|
||||||
auto vb_ptr = &*this->voice_bridge; /* read only no lock needed */
|
|
||||||
std::thread([&, vb_ptr, lock = this->ref()]{
|
|
||||||
unique_lock vbl{this->voice_bridge_lock};
|
|
||||||
if(&*this->voice_bridge == vb_ptr) {
|
|
||||||
auto bridge = std::exchange(this->voice_bridge, nullptr);
|
|
||||||
vbl.unlock();
|
|
||||||
bridge.reset();
|
|
||||||
}
|
|
||||||
}).detach();
|
|
||||||
|
|
||||||
Json::Value response;
|
|
||||||
response["type"] = "WebRTC";
|
|
||||||
response["request"] = "status";
|
|
||||||
response["state"] = "failed";
|
|
||||||
response["reason"] = "voice bridge setup failed!";
|
|
||||||
response["allow_reconnect"] = true;
|
|
||||||
this->sendJson(response);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->voice_bridge->callback_ice_candidate = [&](const rtc::IceCandidate& ice) {
|
|
||||||
Json::Value jsonCandidate;
|
|
||||||
jsonCandidate["type"] = "WebRTC";
|
|
||||||
jsonCandidate["request"] = "ice";
|
|
||||||
jsonCandidate["msg"]["candidate"] = ice.candidate;
|
|
||||||
jsonCandidate["msg"]["sdpMid"] = ice.sdpMid;
|
|
||||||
jsonCandidate["msg"]["sdpMLineIndex"] = ice.sdpMLineIndex;
|
|
||||||
|
|
||||||
this->sendJson(jsonCandidate);
|
|
||||||
};
|
|
||||||
this->voice_bridge->callback_ice_candidate_finished = [&](const std::string& sdpMid, int sdpMLineIndex){
|
|
||||||
Json::Value jsonCandidate;
|
|
||||||
jsonCandidate["type"] = "WebRTC";
|
|
||||||
jsonCandidate["request"] = "ice_finish";
|
|
||||||
jsonCandidate["msg"]["candidate"] = "";
|
|
||||||
jsonCandidate["msg"]["sdpMid"] = sdpMid;
|
|
||||||
jsonCandidate["msg"]["sdpMLineIndex"] = sdpMLineIndex;
|
|
||||||
|
|
||||||
this->sendJson(jsonCandidate);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto vbp = &*this->voice_bridge;
|
|
||||||
voice_bridge_lock_.unlock();
|
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
|
||||||
|
|
||||||
if(vbp != &*this->voice_bridge) {
|
|
||||||
Json::Value response;
|
|
||||||
response["type"] = "WebRTC";
|
|
||||||
response["request"] = "status";
|
|
||||||
response["state"] = "failed";
|
|
||||||
response["reason"] = "initialize failed (obsolete bridge)";
|
|
||||||
response["allow_reconnect"] = true;
|
|
||||||
this->sendJson(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
string error;
|
|
||||||
if(!this->voice_bridge->initialize(error)) {
|
|
||||||
Json::Value response;
|
|
||||||
response["type"] = "WebRTC";
|
|
||||||
response["request"] = "status";
|
|
||||||
response["state"] = "failed";
|
|
||||||
response["reason"] = "initialize failed (" + error + ")";
|
|
||||||
response["allow_reconnect"] = true;
|
|
||||||
this->sendJson(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!this->voice_bridge->parse_offer(val["msg"]["sdp"].asString())) {
|
|
||||||
Json::Value response;
|
|
||||||
response["type"] = "WebRTC";
|
|
||||||
response["request"] = "status";
|
|
||||||
response["state"] = "failed";
|
|
||||||
response["reason"] = "offer apply failed (" + error + ")";
|
|
||||||
response["allow_reconnect"] = true;
|
|
||||||
this->sendJson(response);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
auto sdp_response = this->voice_bridge->generate_answer();
|
|
||||||
Json::Value response;
|
|
||||||
response["type"] = "WebRTC";
|
|
||||||
response["request"] = "answer";
|
|
||||||
response["msg"]["sdp"] = sdp_response;
|
|
||||||
response["msg"]["type"] = "answer";
|
|
||||||
this->sendJson(response);
|
|
||||||
}
|
|
||||||
} else if(subType == "ice") {
|
|
||||||
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
|
|
||||||
if(!this->voice_bridge) {
|
|
||||||
debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
auto candidate_string = val["msg"]["candidate"].asString();
|
|
||||||
auto sdp_mid = val["msg"]["sdpMid"].asString();
|
|
||||||
auto sdp_line_index = val["msg"]["sdpMLineIndex"].asInt();
|
|
||||||
|
|
||||||
deque<shared_ptr<rtc::IceCandidate>> candidates;
|
|
||||||
candidates.push_back(make_shared<rtc::IceCandidate>(candidate_string, sdp_mid, sdp_line_index));
|
|
||||||
|
|
||||||
auto result = this->voice_bridge->apply_ice(candidates);
|
|
||||||
if(result != candidates.size()) {
|
|
||||||
logError(this->getServerId(),
|
|
||||||
"[{}] Failed to apply remote ICE candidate, result: {}. Channel: {} ({}). Candidate: {}",
|
|
||||||
CLIENT_STR_LOG_PREFIX_(this),
|
|
||||||
result,
|
|
||||||
sdp_line_index,
|
|
||||||
sdp_mid,
|
|
||||||
candidate_string
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logTrace(this->getServerId(), "[{}] Successfully added ICE candidate for channel {} ({}).", CLIENT_STR_LOG_PREFIX_(this), sdp_line_index, sdp_mid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(subType == "ice_finish") {
|
|
||||||
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
|
|
||||||
if(!this->voice_bridge) {
|
|
||||||
debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->voice_bridge->remote_ice_finished();
|
|
||||||
}
|
|
||||||
} else if(val["type"].asString() == "ping") {
|
} else if(val["type"].asString() == "ping") {
|
||||||
Json::Value response;
|
Json::Value response;
|
||||||
response["type"] = "pong";
|
response["type"] = "pong";
|
||||||
|
@ -703,50 +518,17 @@ bool WebClient::disconnect(const std::string &reason) {
|
||||||
|
|
||||||
command_result WebClient::handleCommandClientInit(Command &command) {
|
command_result WebClient::handleCommandClientInit(Command &command) {
|
||||||
if(!config::server::clients::teaweb)
|
if(!config::server::clients::teaweb)
|
||||||
return command_result{error::client_type_is_not_allowed};
|
return command_result{error::client_type_is_not_allowed, config::server::clients::teaweb_not_allowed_message};
|
||||||
|
|
||||||
return SpeakingClient::handleCommandClientInit(command);
|
return SpeakingClient::handleCommandClientInit(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
|
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
|
||||||
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
|
|
||||||
|
|
||||||
return SpeakingClient::shouldReceiveVoice(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
||||||
std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
/* Should never be called! */
|
||||||
if(this->voice_bridge) {
|
|
||||||
auto channel = this->voice_bridge->voice_channel();
|
|
||||||
if(channel) {
|
|
||||||
channel->send(view);
|
|
||||||
read_voice_bridge_lock.unlock();
|
|
||||||
|
|
||||||
/* may somehow get the "real" packet size? */
|
|
||||||
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) {
|
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) {
|
||||||
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
|
/* Should never be called! */
|
||||||
if(this->voice_bridge) {
|
|
||||||
auto channel = this->voice_bridge->voice_whisper_channel();
|
|
||||||
if(channel) {
|
|
||||||
uint8_t buffer[teaspeak_packet.length() + 1];
|
|
||||||
memcpy(buffer + 1, teaspeak_packet.data_ptr(), teaspeak_packet.length());
|
|
||||||
buffer[0] = 0;
|
|
||||||
if(flags.head) {
|
|
||||||
buffer[0] |= 0x1U;
|
|
||||||
}
|
|
||||||
channel->send(pipes::buffer{buffer, teaspeak_packet.length() + 1});
|
|
||||||
read_voice_bridge_lock.unlock();
|
|
||||||
|
|
||||||
/* may somehow get the "real" packet size? */
|
|
||||||
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, teaspeak_packet.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result WebClient::handleCommandSetWhisperTarget(Command &command) {
|
command_result WebClient::handleCommandSetWhisperTarget(Command &command) {
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
#include <src/client/ConnectedClient.h>
|
#include <src/client/ConnectedClient.h>
|
||||||
#include <protocol/buffers.h>
|
#include <protocol/buffers.h>
|
||||||
#include "misc/queue.h"
|
#include "misc/queue.h"
|
||||||
#include "SampleHandler.h"
|
|
||||||
#include <opus/opus.h>
|
#include <opus/opus.h>
|
||||||
#include "VoiceBridge.h"
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <EventLoop.h>
|
#include <EventLoop.h>
|
||||||
|
|
||||||
|
@ -30,8 +28,6 @@ namespace ts::server {
|
||||||
bool disconnect(const std::string &reason) override;
|
bool disconnect(const std::string &reason) override;
|
||||||
bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
|
bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
|
||||||
|
|
||||||
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] inline std::chrono::nanoseconds client_ping() const { return this->client_ping_layer_7(); }
|
[[nodiscard]] inline std::chrono::nanoseconds client_ping() const { return this->client_ping_layer_7(); }
|
||||||
[[nodiscard]] inline std::chrono::nanoseconds client_ping_layer_5() const { return this->ping.value; }
|
[[nodiscard]] inline std::chrono::nanoseconds client_ping_layer_5() const { return this->ping.value; }
|
||||||
|
@ -44,8 +40,6 @@ namespace ts::server {
|
||||||
private:
|
private:
|
||||||
WebControlServer* handle;
|
WebControlServer* handle;
|
||||||
|
|
||||||
std::shared_mutex voice_bridge_lock;
|
|
||||||
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
|
||||||
int file_descriptor;
|
int file_descriptor;
|
||||||
|
|
||||||
bool allow_raw_commands{false};
|
bool allow_raw_commands{false};
|
||||||
|
@ -54,7 +48,7 @@ namespace ts::server {
|
||||||
pipes::SSL ssl_handler;
|
pipes::SSL ssl_handler;
|
||||||
pipes::WebSocket ws_handler;
|
pipes::WebSocket ws_handler;
|
||||||
|
|
||||||
std::mutex event_lock;
|
std::mutex event_mutex;
|
||||||
::event* readEvent;
|
::event* readEvent;
|
||||||
::event* writeEvent;
|
::event* writeEvent;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ if(!result && result.msg().find(ignore) == string::npos){
|
||||||
|
|
||||||
#define RESIZE_COLUMN(tblName, rowName, size) up vote EXECUTE("Could not change column size", "ALTER TABLE " tblName " ALTER COLUMN " rowName " varchar(" size ")");
|
#define RESIZE_COLUMN(tblName, rowName, size) up vote EXECUTE("Could not change column size", "ALTER TABLE " tblName " ALTER COLUMN " rowName " varchar(" size ")");
|
||||||
|
|
||||||
#define CURRENT_DATABASE_VERSION 16
|
#define CURRENT_DATABASE_VERSION 17
|
||||||
#define CURRENT_PERMISSION_VERSION 5
|
#define CURRENT_PERMISSION_VERSION 5
|
||||||
|
|
||||||
#define CLIENT_UID_LENGTH "64"
|
#define CLIENT_UID_LENGTH "64"
|
||||||
|
@ -567,6 +567,17 @@ bool SqlDataManager::update_database(std::string &error) {
|
||||||
db_version(16);
|
db_version(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 16: {
|
||||||
|
constexpr static std::array<std::string_view, 1> kUpdateCommands{
|
||||||
|
"UPDATE `properties` SET `value` = '4' WHERE `key` = 'channel_codec' AND (`value` != '4' OR `value` != '5');",
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!execute_commands(this->sql(), error, kUpdateCommands))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
db_version(17);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ threads::ThreadPool MusicBotManager::load_music{4, "music loader "};
|
||||||
|
|
||||||
void MusicBotManager::adjustTickPool() {
|
void MusicBotManager::adjustTickPool() {
|
||||||
size_t bots = 0;
|
size_t bots = 0;
|
||||||
for(const auto& server : serverInstance->getVoiceServerManager()->serverInstances()) bots += server->musicManager->current_bot_count();
|
for(const auto& server : serverInstance->getVoiceServerManager()->serverInstances()) bots += server->music_manager_->current_bot_count();
|
||||||
if(bots == 0)
|
if(bots == 0)
|
||||||
tick_music.setThreads(1);
|
tick_music.setThreads(1);
|
||||||
else
|
else
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Attention: Do not call any librtc functions within being in the callback, only librtc_destroy_client is allowed */
|
||||||
|
struct NativeCallbacks {
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
void(*free_client_data)(const void*);
|
||||||
|
|
||||||
|
void(*client_stream_assignment)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */);
|
||||||
|
void(*client_offer_generated)(const void* /* callback data */, const char* /* offer */, size_t /* offer length */);
|
||||||
|
|
||||||
|
void(*client_stream_start)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */);
|
||||||
|
void(*client_stream_stop)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */);
|
||||||
|
|
||||||
|
void(*client_audio_sender_data)(const void* /* callback data */, const void* /* source callback data */, uint8_t /* mode */, uint16_t /* seq. no. */, uint8_t /* codec */, const void* /* data */, uint32_t /* length */);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char* librtc_version();
|
||||||
|
extern void librtc_free_str(const char* /* ptr */);
|
||||||
|
|
||||||
|
extern const char* librtc_init(const NativeCallbacks* /* */, size_t /* size of the callback struct */);
|
||||||
|
|
||||||
|
extern void* librtc_create_server();
|
||||||
|
extern void librtc_destroy_server(void* /* server */);
|
||||||
|
|
||||||
|
extern uint32_t librtc_create_rtp_client(void* /* server */, void* /* callback data */);
|
||||||
|
extern uint32_t librtc_create_native_client(void* /* server */, void* /* callback data */);
|
||||||
|
extern void librtc_destroy_client(void* /* server */, uint32_t /* client id */);
|
||||||
|
|
||||||
|
extern const char* librtc_reset_rtp_session(void* /* server */, uint32_t /* client id */);
|
||||||
|
extern const char* librtc_apply_remote_description(void* /* server */, uint32_t /* client id */, uint32_t /* mode */, const char* /* description */);
|
||||||
|
extern const char* librtc_generate_local_description(void* /* server */, uint32_t /* client id */, char** /* description */);
|
||||||
|
extern const char* librtc_add_ice_candidate(void* /* server */, uint32_t /* client id */, uint32_t /* media line */, const char* /* candidate */);
|
||||||
|
|
||||||
|
extern void* librtc_create_audio_source_supplier(void* /* server */, uint32_t /* client id */);
|
||||||
|
extern void librtc_audio_source_supply(void* /* sender */,
|
||||||
|
uint16_t /* seq no */,
|
||||||
|
bool /* marked */,
|
||||||
|
uint32_t /* timestamp */,
|
||||||
|
uint8_t /* codec */,
|
||||||
|
const void* /* data */,
|
||||||
|
uint32_t /* length */);
|
||||||
|
extern void librtc_destroy_audio_source_supplier(void* /* sender */);
|
||||||
|
|
||||||
|
extern uint32_t librtc_create_channel(void* /* server */);
|
||||||
|
extern uint32_t librtc_assign_channel(void* /* server */, uint32_t /* client id */, uint32_t /* channel id */);
|
||||||
|
extern uint32_t librtc_client_broadcast(void* /* server */, uint32_t /* client id */, uint8_t /* broadcast type */, uint32_t /* stream id */);
|
||||||
|
extern void librtc_destroy_channel(void* /* server */, uint32_t /* channel */);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -0,0 +1,281 @@
|
||||||
|
//
|
||||||
|
// Created by WolverinDEV on 24/10/2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "./lib.h"
|
||||||
|
#include "./imports.h"
|
||||||
|
#include "../client/SpeakingClient.h"
|
||||||
|
#include "../client/voice/VoiceClient.h"
|
||||||
|
#include <Definitions.h>
|
||||||
|
|
||||||
|
using namespace ts;
|
||||||
|
using namespace ts::server;
|
||||||
|
using namespace ts::rtc;
|
||||||
|
|
||||||
|
struct LibCallbackData {
|
||||||
|
ClientId client_id;
|
||||||
|
std::weak_ptr<SpeakingClient> weak_ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
void librtc_callback_free_client_data(const void* data) {
|
||||||
|
delete (LibCallbackData*) data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void librtc_callback_client_stream_assignment(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
||||||
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
||||||
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
||||||
|
|
||||||
|
auto target_client = callback_data->weak_ref.lock();
|
||||||
|
if(!target_client) { return; }
|
||||||
|
|
||||||
|
if(source_data) {
|
||||||
|
auto source_client = source_data->weak_ref.lock();
|
||||||
|
if(!source_client) { return; }
|
||||||
|
|
||||||
|
ts::command_builder notify{"notifyrtcstreamassignment"};
|
||||||
|
notify.put_unchecked(0, "streamid", stream_id);
|
||||||
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
||||||
|
notify.put_unchecked(0, "scluid", source_client->getUid());
|
||||||
|
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
|
||||||
|
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
|
||||||
|
target_client->sendCommand(notify);
|
||||||
|
} else {
|
||||||
|
ts::command_builder notify{"notifyrtcstreamassignment"};
|
||||||
|
notify.put_unchecked(0, "streamid", stream_id);
|
||||||
|
notify.put_unchecked(0, "sclid", 0);
|
||||||
|
target_client->sendCommand(notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void librtc_callback_client_offer_generated(const void* callback_data_ptr, const char* offer, size_t offer_length) {
|
||||||
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
||||||
|
auto target_client = callback_data->weak_ref.lock();
|
||||||
|
if(!target_client) { return; }
|
||||||
|
|
||||||
|
ts::command_builder notify{"notifyrtcsessiondescription"};
|
||||||
|
notify.put_unchecked(0, "mode", "offer");
|
||||||
|
notify.put_unchecked(0, "sdp", std::string_view{offer, offer_length});
|
||||||
|
target_client->sendCommand(notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void librtc_callback_client_audio_start(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
||||||
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
||||||
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
||||||
|
|
||||||
|
auto target_client = callback_data->weak_ref.lock();
|
||||||
|
if(!target_client) { return; }
|
||||||
|
|
||||||
|
auto source_client = source_data->weak_ref.lock();
|
||||||
|
if(!source_client) { return; }
|
||||||
|
|
||||||
|
ts::command_builder notify{"notifyrtcstateaudio"};
|
||||||
|
notify.put_unchecked(0, "streamid", stream_id);
|
||||||
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
||||||
|
notify.put_unchecked(0, "scluid", source_client->getUid());
|
||||||
|
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
|
||||||
|
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
|
||||||
|
notify.put_unchecked(0, "state", "1");
|
||||||
|
target_client->sendCommand(notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void librtc_callback_client_audio_stop(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
|
||||||
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
||||||
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
||||||
|
|
||||||
|
auto target_client = callback_data->weak_ref.lock();
|
||||||
|
if(!target_client) { return; }
|
||||||
|
|
||||||
|
auto source_client = source_data->weak_ref.lock();
|
||||||
|
if(!source_client) { return; }
|
||||||
|
|
||||||
|
ts::command_builder notify{"notifyrtcstateaudio"};
|
||||||
|
notify.put_unchecked(0, "streamid", stream_id);
|
||||||
|
notify.put_unchecked(0, "sclid", source_client->getClientId());
|
||||||
|
notify.put_unchecked(0, "state", "0");
|
||||||
|
target_client->sendCommand(notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void librtc_callback_client_audio_sender_data(const void* callback_data_ptr, const void* source_data_ptr, uint8_t mode, uint16_t seq_no, uint8_t codec, const void* data, uint32_t length) {
|
||||||
|
auto callback_data = (LibCallbackData*) callback_data_ptr;
|
||||||
|
auto source_data = (LibCallbackData*) source_data_ptr;
|
||||||
|
|
||||||
|
/* Target client must be a voice client. The web client does not uses the native audio client */
|
||||||
|
auto target_client = std::dynamic_pointer_cast<VoiceClient>(callback_data->weak_ref.lock());
|
||||||
|
if(!target_client) { return; }
|
||||||
|
|
||||||
|
auto source_client = source_data->weak_ref.lock();
|
||||||
|
if(!source_client) { return; }
|
||||||
|
|
||||||
|
if(mode == 0) {
|
||||||
|
if(!target_client->shouldReceiveVoice(source_client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Somehow set the head (compressed) flag for beginning voice packets? */
|
||||||
|
auto packet = protocol::allocate_outgoing_packet(length + 5);
|
||||||
|
packet->type_and_flags = protocol::PacketType::VOICE;
|
||||||
|
|
||||||
|
*((uint16_t*) packet->payload + 0) = htons(seq_no);
|
||||||
|
*((uint16_t*) packet->payload + 1) = htons(source_data->client_id);
|
||||||
|
packet->payload[4] = codec;
|
||||||
|
if(data) {
|
||||||
|
memcpy(packet->payload + 5, data, length);
|
||||||
|
} else {
|
||||||
|
assert(length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
target_client->getConnection()->send_packet(packet);
|
||||||
|
} else {
|
||||||
|
if(!target_client->shouldReceiveVoiceWhisper(source_client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: TODO! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NativeCallbacks native_callbacks{
|
||||||
|
.version = 1,
|
||||||
|
|
||||||
|
.free_client_data = librtc_callback_free_client_data,
|
||||||
|
|
||||||
|
.client_stream_assignment = librtc_callback_client_stream_assignment,
|
||||||
|
.client_offer_generated = librtc_callback_client_offer_generated,
|
||||||
|
|
||||||
|
.client_stream_start = librtc_callback_client_audio_start,
|
||||||
|
.client_stream_stop = librtc_callback_client_audio_stop,
|
||||||
|
|
||||||
|
.client_audio_sender_data = librtc_callback_client_audio_sender_data
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view rtc::version() {
|
||||||
|
return std::string_view{librtc_version()};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rtc::initialize(std::string &error) {
|
||||||
|
auto error_ptr = librtc_init(&native_callbacks, sizeof native_callbacks);
|
||||||
|
if(!error_ptr) { return true; }
|
||||||
|
|
||||||
|
error = std::string{error_ptr};
|
||||||
|
librtc_free_str(error_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::Server() {
|
||||||
|
this->server_ptr = librtc_create_server();
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::~Server() {
|
||||||
|
librtc_destroy_server(this->server_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
RTCClientId Server::create_rtp_client(const std::shared_ptr<server::SpeakingClient> &client) {
|
||||||
|
auto data = new LibCallbackData{
|
||||||
|
.client_id = client->getClientId(),
|
||||||
|
.weak_ref = client
|
||||||
|
};
|
||||||
|
return librtc_create_rtp_client(this->server_ptr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
RTCClientId Server::create_native_client(const std::shared_ptr<server::SpeakingClient> &client) {
|
||||||
|
auto data = new LibCallbackData{
|
||||||
|
.client_id = client->getClientId(),
|
||||||
|
.weak_ref = client
|
||||||
|
};
|
||||||
|
return librtc_create_native_client(this->server_ptr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::destroy_client(RTCClientId client_id) {
|
||||||
|
librtc_destroy_client(this->server_ptr, client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::reset_rtp_session(RTCClientId client_id) {
|
||||||
|
librtc_reset_rtp_session(this->server_ptr, client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::apply_remote_description(std::string &error, RTCClientId client_id, uint32_t mode, const std::string_view &description) {
|
||||||
|
auto error_ptr = librtc_apply_remote_description(this->server_ptr, client_id, mode, description.data());
|
||||||
|
if(!error_ptr) { return true; }
|
||||||
|
|
||||||
|
error = std::string{error_ptr};
|
||||||
|
librtc_free_str(error_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::generate_local_description(RTCClientId client, std::string &result) {
|
||||||
|
char* description_ptr;
|
||||||
|
auto error_ptr = librtc_generate_local_description(this->server_ptr, client, &description_ptr);
|
||||||
|
if(error_ptr) {
|
||||||
|
result = std::string{error_ptr};
|
||||||
|
librtc_free_str(error_ptr);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
result = std::string{description_ptr};
|
||||||
|
librtc_free_str(description_ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::add_ice_candidate(std::string &error, RTCClientId client_id, uint32_t media_line, const std::string_view &description) {
|
||||||
|
auto error_ptr = librtc_add_ice_candidate(this->server_ptr, client_id, media_line, description.length() == 0 ? nullptr : description.data());
|
||||||
|
if(!error_ptr) { return true; }
|
||||||
|
|
||||||
|
error = std::string{error_ptr};
|
||||||
|
librtc_free_str(error_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::ice_candidates_finished(RTCClientId) {
|
||||||
|
/* Nothing really to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Server::create_channel() {
|
||||||
|
return librtc_create_channel(this->server_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelAssignResult Server::assign_channel(uint32_t client_id, uint32_t channel_id) {
|
||||||
|
auto result = librtc_assign_channel(this->server_ptr, client_id, channel_id);
|
||||||
|
switch(result) {
|
||||||
|
case 0x00: return ChannelAssignResult::Success;
|
||||||
|
case 0x01: return ChannelAssignResult::ClientUnknown;
|
||||||
|
case 0x02: return ChannelAssignResult::TargetChannelUnknown;
|
||||||
|
default: return ChannelAssignResult::UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BroadcastStartResult Server::start_broadcast(uint32_t client_id, uint8_t btype, uint32_t track_id) {
|
||||||
|
auto result = librtc_client_broadcast(this->server_ptr, client_id, btype, track_id);
|
||||||
|
switch(result) {
|
||||||
|
case 0x00: return BroadcastStartResult::Success;
|
||||||
|
case 0x01: return BroadcastStartResult::InvalidClient;
|
||||||
|
case 0x02: return BroadcastStartResult::ClientHasNoChannel;
|
||||||
|
case 0x03: return BroadcastStartResult::InvalidBroadcastType;
|
||||||
|
case 0x04: return BroadcastStartResult::InvalidStreamId;
|
||||||
|
default: return BroadcastStartResult::UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<NativeAudioSourceSupplier> Server::create_audio_source_supplier_sender(uint32_t client_id) {
|
||||||
|
auto result = librtc_create_audio_source_supplier(this->server_ptr, client_id);
|
||||||
|
if(!result) { return std::nullopt; }
|
||||||
|
|
||||||
|
return std::make_optional<NativeAudioSourceSupplier>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::destroy_channel(uint32_t channel_id) {
|
||||||
|
librtc_destroy_channel(this->server_ptr, channel_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeAudioSourceSupplier::NativeAudioSourceSupplier(void *ptr) : sender_ptr{ptr} {}
|
||||||
|
NativeAudioSourceSupplier::NativeAudioSourceSupplier(NativeAudioSourceSupplier &&other) noexcept : sender_ptr{other.sender_ptr} {
|
||||||
|
other.sender_ptr = nullptr;
|
||||||
|
}
|
||||||
|
NativeAudioSourceSupplier::~NativeAudioSourceSupplier() noexcept {
|
||||||
|
if(this->sender_ptr) {
|
||||||
|
librtc_destroy_audio_source_supplier(this->sender_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeAudioSourceSupplier::send_audio(uint16_t seq_no, bool marked, uint32_t timestamp, uint8_t codec, const std::string_view &data) {
|
||||||
|
librtc_audio_source_supply(this->sender_ptr, seq_no, marked, timestamp, codec, data.empty() ? nullptr : data.data(), data.length());
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ts::server {
|
||||||
|
class SpeakingClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ts::rtc {
|
||||||
|
typedef uint32_t RTCClientId;
|
||||||
|
typedef uint32_t RTCChannelId;
|
||||||
|
typedef uint32_t RTCStreamId;
|
||||||
|
|
||||||
|
extern std::string_view version();
|
||||||
|
extern bool initialize(std::string& /* error */);
|
||||||
|
|
||||||
|
enum struct ChannelAssignResult {
|
||||||
|
Success,
|
||||||
|
ClientUnknown,
|
||||||
|
TargetChannelUnknown,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum struct BroadcastStartResult {
|
||||||
|
Success,
|
||||||
|
InvalidClient,
|
||||||
|
ClientHasNoChannel,
|
||||||
|
InvalidBroadcastType,
|
||||||
|
InvalidStreamId,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
|
class NativeAudioSourceSupplier;
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
Server();
|
||||||
|
~Server();
|
||||||
|
|
||||||
|
RTCClientId create_rtp_client(const std::shared_ptr<server::SpeakingClient>& /* client */);
|
||||||
|
RTCClientId create_native_client(const std::shared_ptr<server::SpeakingClient>& /* client */);
|
||||||
|
void destroy_client(RTCClientId /* client id */);
|
||||||
|
|
||||||
|
/* RTC client actions */
|
||||||
|
void reset_rtp_session(RTCClientId /* client */);
|
||||||
|
bool apply_remote_description(std::string& /* error */, RTCClientId /* client id */, uint32_t /* mode */, const std::string_view& /* description */);
|
||||||
|
bool generate_local_description(RTCClientId /* client id */, std::string& /* result */);
|
||||||
|
bool add_ice_candidate(std::string& /* error */, RTCClientId /* client id */, uint32_t /* media line */, const std::string_view& /* description */);
|
||||||
|
void ice_candidates_finished(RTCClientId /* client id */);
|
||||||
|
|
||||||
|
/* Native client actions */
|
||||||
|
std::optional<NativeAudioSourceSupplier> create_audio_source_supplier_sender(RTCClientId /* client id */);
|
||||||
|
|
||||||
|
/* channel actions */
|
||||||
|
uint32_t create_channel();
|
||||||
|
ChannelAssignResult assign_channel(RTCClientId /* client id */, RTCChannelId /* channel id */);
|
||||||
|
BroadcastStartResult start_broadcast(RTCClientId /* client id */, uint8_t /* broadcast type */, RTCStreamId /* stream id */);
|
||||||
|
void destroy_channel(RTCChannelId /* channel id */);
|
||||||
|
private:
|
||||||
|
void* server_ptr{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class NativeAudioSourceSupplier {
|
||||||
|
public:
|
||||||
|
explicit NativeAudioSourceSupplier() : sender_ptr{nullptr} {}
|
||||||
|
explicit NativeAudioSourceSupplier(void*);
|
||||||
|
virtual ~NativeAudioSourceSupplier() noexcept;
|
||||||
|
|
||||||
|
NativeAudioSourceSupplier(const NativeAudioSourceSupplier&) = delete;
|
||||||
|
NativeAudioSourceSupplier(NativeAudioSourceSupplier&& other) noexcept;
|
||||||
|
|
||||||
|
inline void reset(NativeAudioSourceSupplier& other) {
|
||||||
|
std::swap(other.sender_ptr, this->sender_ptr);
|
||||||
|
}
|
||||||
|
void send_audio(uint16_t /* seq no */, bool /* marked */, uint32_t /* timestamp */, uint8_t /* codec */, const std::string_view& /* data */);
|
||||||
|
private:
|
||||||
|
void* sender_ptr;
|
||||||
|
};
|
||||||
|
}
|
|
@ -115,7 +115,9 @@ void VoiceServer::triggerWrite(const std::shared_ptr<VoiceClient>& client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->io->invoke_write(client);
|
if(auto io{this->io}; io) {
|
||||||
|
io->invoke_write(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceServer::schedule_command_handling(const ts::server::VoiceClient *client) {
|
void VoiceServer::schedule_command_handling(const ts::server::VoiceClient *client) {
|
||||||
|
|
2
shared
2
shared
|
@ -1 +1 @@
|
||||||
Subproject commit f5c643129e306132437e8b529c2398e9cfc0545a
|
Subproject commit 7b51bcc5e82802ea8173da8c341ff31614f1c49b
|
Loading…
Reference in New Issue