From 4f5a4dc99319ed17b7e733a437dfa79a64b2f145 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sun, 6 Sep 2020 21:00:27 +0200 Subject: [PATCH] Some more updates --- git-teaspeak | 2 +- server/CMakeLists.txt | 2 +- server/src/client/SpeakingClient.cpp | 280 ++++++++++-------- server/src/client/SpeakingClient.h | 4 +- server/src/client/command_handler/client.cpp | 2 +- server/src/client/command_handler/misc.cpp | 1 + server/src/client/voice/VoiceClient.cpp | 8 +- server/src/client/voice/VoiceClient.h | 6 +- .../VoiceClientConnectionPacketHandler.cpp | 2 +- server/src/client/web/VoiceBridge.cpp | 86 ++++-- server/src/client/web/VoiceBridge.h | 39 ++- server/src/client/web/WSWebClient.cpp | 13 +- server/src/client/web/WebClient.cpp | 166 +++++++++-- server/src/client/web/WebClient.h | 40 ++- server/src/server/VoiceServer.cpp | 2 +- shared | 2 +- 16 files changed, 453 insertions(+), 202 deletions(-) diff --git a/git-teaspeak b/git-teaspeak index b83a798..8fbf120 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit b83a798e5348cbf916b84c1ab684adbadbaf9a71 +Subproject commit 8fbf120f756d603da38029843a8844a743440bac diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 261bfa1..672aeb1 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -257,7 +257,7 @@ target_link_libraries(PermMapHelper SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MINOR "4") -SET(CPACK_PACKAGE_VERSION_PATCH "19") +SET(CPACK_PACKAGE_VERSION_PATCH "20") if (BUILD_TYPE_NAME EQUAL OFF) SET(CPACK_PACKAGE_VERSION_DATA "beta") elseif (BUILD_TYPE_NAME STREQUAL "") diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 3145260..c51cbb4 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -20,6 +20,9 @@ using namespace ts::protocol; //#define PKT_LOG_VOICE //#define PKT_LOG_WHISPER +constexpr static auto kMaxWhisperClientNameLength{30}; +constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */ +constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength}; bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr &sender) { //if(this->properties()[property::CLIENT_AWAY].as()) return false; @@ -150,42 +153,35 @@ inline bool update_whisper_error(std::chrono::system_clock::time_point& last) { //Server group => type := SERVER_GROUP and target_id := //Channel group => type := CHANNEL_GROUP and target_id := //Channel commander => type := CHANNEL_COMMANDER and target_id := 0 -void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bool new_packet) { - if(data.length() < 5) { +void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& payload, bool new_packet, bool head) { + if(payload.length() < 5) { this->disconnect("Invalid packet (Voice whisper)"); - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, payload.length()); return; } - uint16_t offset = 0; - auto vpacketId = be2le16((char*) data.data_ptr(), offset, &offset); - auto codec = (uint8_t) data[offset++]; + uint16_t payload_offset{0}; + auto voice_packet_id = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset); + auto voice_codec = (uint8_t) payload[payload_offset++]; - VoicePacketFlags flags{}; - flags.head = false; - flags.fragmented = false; - flags.new_protocol = new_packet; + std::deque> target_clients; if(new_packet) { - if(data.length() < 7) { + if(payload.length() < 7) { this->disconnect("Invalid packet (Voice whisper | New)"); - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, payload.length()); return; } - auto type = (WhisperType) data[offset++]; - auto target = (WhisperTarget) data[offset++]; - auto type_id = be2le64((char*) data.data_ptr(), offset, &offset); - this->resetIdleTime(); + auto type = (WhisperType) payload[payload_offset++]; + auto target = (WhisperTarget) payload[payload_offset++]; + auto type_id = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset); - size_t data_length = data.length() - offset; #ifdef PKT_LOG_WHISPER logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id); #endif - - deque> available_clients; if(type == WhisperType::ECHO) { - available_clients.push_back(dynamic_pointer_cast(this->ref())); + target_clients.push_back(dynamic_pointer_cast(this->ref())); } else { for(const auto& client : this->server->getClients()) { auto speakingClient = dynamic_pointer_cast(client); @@ -193,84 +189,82 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo if(!speakingClient->currentChannel) continue; if(type == WhisperType::ALL) { - available_clients.push_back(speakingClient); + target_clients.push_back(speakingClient); } else if(type == WhisperType::SERVER_GROUP) { if(type_id == 0) - available_clients.push_back(speakingClient); + target_clients.push_back(speakingClient); else { shared_lock client_lock(this->channel_lock); for(const auto& id : client->cached_server_groups) { if(id == type_id) { - available_clients.push_back(speakingClient); + target_clients.push_back(speakingClient); break; } } } } else if(type == WhisperType::CHANNEL_GROUP) { if(client->cached_channel_group == type_id) - available_clients.push_back(speakingClient); + target_clients.push_back(speakingClient); } else if(type == WhisperType::CHANNEL_COMMANDER) { if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as()) - available_clients.push_back(speakingClient); + target_clients.push_back(speakingClient); } } - - if(target == WhisperTarget::CHANNEL_CURRENT) { - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { return target->currentChannel != this->currentChannel; - }), available_clients.end()); + }), target_clients.end()); } else if(target == WhisperTarget::CHANNEL_PARENT) { auto current_parent = this->currentChannel->parent(); if(!current_parent) return; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { return target->currentChannel != current_parent; - }), available_clients.end()); + }), target_clients.end()); } else if(target == WhisperTarget::CHANNEL_ALL_PARENT) { shared_ptr current_parent; { current_parent = this->currentChannel->parent(); if(!current_parent) return; } - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { auto tmp_parent = current_parent; while(tmp_parent && tmp_parent != target->currentChannel) tmp_parent = tmp_parent->parent(); return target->currentChannel != tmp_parent; - }), available_clients.end()); + }), target_clients.end()); } else if(target == WhisperTarget::CHANNEL_FAMILY) { shared_ptr current = this->currentChannel; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { auto tmp_current = target->currentChannel; while(tmp_current && tmp_current != current) tmp_current = tmp_current->parent(); return current != tmp_current; - }), available_clients.end()); + }), target_clients.end()); } else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) { shared_ptr current = this->currentChannel; while(current && current->parent()) current = current->parent(); - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { auto tmp_current = target->currentChannel; while(tmp_current && tmp_current != current) tmp_current = tmp_current->parent(); return current != tmp_current; - }), available_clients.end()); + }), target_clients.end()); } else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) { shared_ptr current = this->currentChannel; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& target) { return target->currentChannel->parent() != current; - }), available_clients.end()); + }), target_clients.end()); } auto self_lock = this->_this.lock(); - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr& cl) { + target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const std::shared_ptr& cl) { auto speakingClient = dynamic_pointer_cast(cl); return !speakingClient->shouldReceiveVoiceWhisper(self_lock); - }), available_clients.end()); + }), target_clients.end()); - if(available_clients.empty()) { + if(target_clients.empty()) { if(update_whisper_error(this->speak_last_no_whisper_target)) { command_result result{error::whisper_no_targets}; this->notifyError(result); @@ -278,7 +272,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo return; } - if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { + if(target_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { command_result result{error::whisper_too_many_targets}; this->notifyError(result); @@ -286,103 +280,143 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo return; } } - - //Create the packet data - char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length]; - if(offset < data.length()) - memcpy(&packet_buffer[OUT_WHISPER_PKT_OFFSET], &data[offset], data_length); - - le2be16(vpacketId, packet_buffer, 0); - le2be16(this->getClientId(), packet_buffer, 2); - packet_buffer[4] = codec; - - VoicePacketFlags flags{}; - auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length); - for(const auto& cl : available_clients){ - cl->send_voice_whisper_packet(data, flags); - } - - this->updateSpeak(false, system_clock::now()); } else { - auto clientCount = (uint8_t) data[offset++]; - auto channelCount = (uint8_t) data[offset++]; - if(data.length() < 5 + channelCount * 2 + clientCount * 8) { - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {} Required: {}", CLIENT_STR_LOG_PREFIX, data.length(), to_string(5 + channelCount * 2 + clientCount * 8)); + auto channelCount = (uint8_t) payload[payload_offset++]; + auto clientCount = (uint8_t) payload[payload_offset++]; + if(payload.length() < 5 + clientCount * 2 + channelCount * 8) { + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {} Required: {}", CLIENT_STR_LOG_PREFIX, payload.length(), to_string(5 + channelCount * 2 + clientCount * 8)); return; } - this->resetIdleTime(); - ChannelId channelIds[clientCount]; - ClientId clientIds[channelCount]; + ChannelId channelIds[channelCount]; + ClientId clientIds[clientCount]; - for(uint8_t index = 0; index < clientCount; index++) - channelIds[index] = be2le64((char*) data.data_ptr(), offset, &offset); - for(uint8_t index = 0; index < channelCount; index++) - clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset); - - auto available_clients = this->server->getClients(); - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr& cl) { - auto speakingClient = dynamic_pointer_cast(cl); - if(!speakingClient || cl == this || !speakingClient->currentChannel) return true; - - auto clientChannelId = cl->currentChannel->channelId(); - auto clientId = cl->getClientId(); - - for(uint8_t index = 0; index < clientCount; index++) - if(channelIds[index] == clientChannelId) return false; - - for(uint8_t index = 0; index < channelCount; index++) - if(clientIds[index] == clientId) return false; - - return true; - }), available_clients.end()); - - auto self_lock = this->_this.lock(); - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr& cl) { - auto speakingClient = dynamic_pointer_cast(cl); - return !speakingClient->shouldReceiveVoiceWhisper(self_lock); - }), available_clients.end()); - - if(available_clients.empty()) { - if(update_whisper_error(this->speak_last_no_whisper_target)) { - command_result result{error::whisper_no_targets}; - this->notifyError(result); - } - return; - } - if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { - if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { - command_result result{error::whisper_too_many_targets}; - this->notifyError(result); - } - return; + for(uint8_t index = 0; index < channelCount; index++) { + channelIds[index] = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset); + } + + for(uint8_t index = 0; index < clientCount; index++) { + clientIds[index] = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset); } - size_t dataLength = data.length() - offset; #ifdef PKT_LOG_WHISPER logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount); #endif + + for(const auto& client : this->server->getClients()) { + auto speaking_client = dynamic_pointer_cast(client); + if(!speaking_client || client == this || !speaking_client->currentChannel) + continue; + + auto clientChannelId = speaking_client->getChannelId(); + auto clientId = speaking_client->getClientId(); + + for(uint8_t index = 0; index < channelCount; index++) { + if(channelIds[index] == clientChannelId) { + goto add_client; + } + } + + for(uint8_t index = 0; index < clientCount; index++) { + if(clientIds[index] == clientId) { + goto add_client; + } + } + + continue; + + add_client: + if(!speaking_client->shouldReceiveVoiceWhisper(this->ref())) { + continue; + } + + target_clients.push_back(speaking_client); + } + } + + if(target_clients.empty()) { + if(update_whisper_error(this->speak_last_no_whisper_target)) { + command_result result{error::whisper_no_targets}; + this->notifyError(result); + } + return; + } + + if(target_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { + if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { + command_result result{error::whisper_too_many_targets}; + this->notifyError(result); + } + return; + } + + /* send the packet */ + { + size_t voice_payload_length = payload.length() - payload_offset; + //Create the packet data - char packetBuffer[OUT_WHISPER_PKT_OFFSET + dataLength]; - if(offset < data.length()) - memcpy(&packetBuffer[OUT_WHISPER_PKT_OFFSET], &data[offset], dataLength); + char whisper_packet_buffer[kWhisperMaxHeaderLength + voice_payload_length]; + size_t whisper_packet_offset{0}; + size_t whisper_packet_teamspeak_offset{0}; - le2be16(vpacketId, packetBuffer, 0); - le2be16(this->getClientId(), packetBuffer, 2); - packetBuffer[4] = codec; + /* writing the teaspeak header */ + if(head) { + auto uniqueId = this->getUid(); + auto nickname = this->getDisplayName(); - VoicePacketFlags flags{}; - auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength); + if(uniqueId.length() > kWhisperClientUniqueIdLength) { + logCritical(LOG_GENERAL, "Clients unique id is longer than the expected max length of {}. Unique length: {}", kWhisperClientUniqueIdLength, uniqueId.length()); + return; + } - for(const auto& cl : available_clients){ //Faster? - auto speakingClient = dynamic_pointer_cast(cl); - assert(speakingClient); - if(speakingClient->shouldReceiveVoiceWhisper(_this.lock())) - speakingClient->send_voice_whisper_packet(data, flags); + if(nickname.length() > kMaxWhisperClientNameLength) { + logCritical(LOG_GENERAL, "Clients name is longer than the expected max length of {}. Name length: {}", kMaxWhisperClientNameLength, nickname.length()); + return; + } + + memset(whisper_packet_buffer + whisper_packet_offset, 0, kWhisperClientUniqueIdLength); + memcpy(whisper_packet_buffer + whisper_packet_offset, uniqueId.data(), uniqueId.length()); + whisper_packet_offset += kWhisperClientUniqueIdLength; + + whisper_packet_buffer[whisper_packet_offset++] = nickname.length(); + memcpy(whisper_packet_buffer + whisper_packet_offset, nickname.data(), nickname.length()); + whisper_packet_offset += nickname.length(); } - this->updateSpeak(false, system_clock::now()); + /* writing the "normal" header and payload */ + { + whisper_packet_teamspeak_offset = whisper_packet_offset; + + *(uint16_t*) &whisper_packet_buffer[whisper_packet_offset] = htons(voice_packet_id); + whisper_packet_offset += 2; + + *(uint16_t*) &whisper_packet_buffer[whisper_packet_offset] = htons(this->getClientId()); + whisper_packet_offset += 2; + + whisper_packet_buffer[whisper_packet_offset++] = voice_codec; + + if(voice_payload_length > 0) { + memcpy(&whisper_packet_buffer[whisper_packet_offset], &payload[payload_offset], voice_payload_length); + whisper_packet_offset += voice_payload_length; + } + } + + VoicePacketFlags flags{}; + flags.head = head; + + pipes::buffer_view teaspeak_packet{}, teamspeak_packet{}; + teaspeak_packet = pipes::buffer_view{whisper_packet_buffer, whisper_packet_offset}; + teamspeak_packet = pipes::buffer_view{whisper_packet_buffer + whisper_packet_teamspeak_offset, whisper_packet_offset - whisper_packet_teamspeak_offset}; + + for(const auto& cl : target_clients) { + if(cl->shouldReceiveVoiceWhisper(_this.lock())) { + cl->send_voice_whisper_packet(teamspeak_packet, teaspeak_packet, flags); + } + } } + + this->resetIdleTime(); + this->updateSpeak(false, std::chrono::system_clock::now()); } #define TEST_PARM(type) \ diff --git a/server/src/client/SpeakingClient.h b/server/src/client/SpeakingClient.h index 4f5baad..355207a 100644 --- a/server/src/client/SpeakingClient.h +++ b/server/src/client/SpeakingClient.h @@ -43,7 +43,7 @@ namespace ts::server { //Whisper bool shouldReceiveVoiceWhisper(const std::shared_ptr &sender); - virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; + virtual void send_voice_whisper_packet(const pipes::buffer_view& /* teamspeak payload */, const pipes::buffer_view& /* teaspeak payload */, const VoicePacketFlags& /* flags */) = 0; inline std::chrono::milliseconds takeSpokenTime() { auto time = this->speak_time; @@ -62,7 +62,7 @@ namespace ts::server { public: void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); - virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); + virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */, bool /* head */); void processJoin(); void processLeave(); diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index b875305..f24089d 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -896,7 +896,7 @@ command_result ConnectedClient::handleCommandClientGetUidFromClid(Command &cmd) bool found = false; auto client_list = this->server->getClients(); - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetuidfromclid" : ""); + Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientuidfromclid" : ""); int result_index = 0; for(int index = 0; index < cmd.bulkCount(); index++) { diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index 0315d07..fc1199a 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -2819,6 +2819,7 @@ command_result ConnectedClient::handleCommandListFeatureSupport(ts::Command &cmd REGISTER_FEATURE("error-bulks", FeatureSupportMode::FULL, 1); REGISTER_FEATURE("advanced-channel-chat", FeatureSupportMode::FULL, 1); REGISTER_FEATURE("log-query", FeatureSupportMode::FULL, 1); + REGISTER_FEATURE("whisper-echo", FeatureSupportMode::FULL, 1); this->sendCommand(notify); return command_result{error::ok}; diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index a01d94f..ec50c8c 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -257,14 +257,18 @@ void VoiceClient::send_voice_packet(const pipes::buffer_view &voice_buffer, cons this->connection->send_packet(PacketType::VOICE, packet_flags, voice_buffer.data_ptr(), voice_buffer.length()); } -void VoiceClient::send_voice_whisper_packet(const pipes::buffer_view &voice_buffer, const SpeakingClient::VoicePacketFlags &flags) { +void VoiceClient::send_voice_whisper_packet(const pipes::buffer_view &teamspeak_packet, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) { PacketFlag::PacketFlags packet_flags{PacketFlag::None}; packet_flags |= flags.encrypted ? 0U : PacketFlag::Unencrypted; packet_flags |= flags.head ? PacketFlag::Compressed : 0U; packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0U; packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0U; - this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, voice_buffer.data_ptr(), voice_buffer.length()); + if(this->getType() == ClientType::CLIENT_TEASPEAK) { + this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teaspeak_packet.data_ptr(), teaspeak_packet.length()); + } else { + this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teamspeak_packet.data_ptr(), teamspeak_packet.length()); + } } float VoiceClient::current_ping_deviation() { diff --git a/server/src/client/voice/VoiceClient.h b/server/src/client/voice/VoiceClient.h index c0d3a54..31e83ec 100644 --- a/server/src/client/voice/VoiceClient.h +++ b/server/src/client/voice/VoiceClient.h @@ -90,7 +90,11 @@ namespace ts { void handlePacketCommand(const pipes::buffer_view&); public: void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; - void send_voice_whisper_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; + void send_voice_whisper_packet( + const pipes::buffer_view &/* teamspeak packet */, + const pipes::buffer_view &/* teaspeak packet */, + const VoicePacketFlags &flags + ) override; protected: virtual command_result handleCommand(Command &command) override; diff --git a/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp b/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp index e4a21da..22a0454 100644 --- a/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp +++ b/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp @@ -35,7 +35,7 @@ void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientP auto client = this->getCurrentClient(); if(!client) return; - client->handlePacketVoiceWhisper(packet.payload(), (packet.flags() & PacketFlag::NewProtocol) > 0); + client->handlePacketVoiceWhisper(packet.payload(), (packet.flags() & PacketFlag::NewProtocol) > 0, (packet.flags() & PacketFlag::Compressed) > 0); } void VoiceClientConnection::handlePacketAck(const protocol::ClientPacketParser& packet) { diff --git a/server/src/client/web/VoiceBridge.cpp b/server/src/client/web/VoiceBridge.cpp index 99c2879..3c0321e 100644 --- a/server/src/client/web/VoiceBridge.cpp +++ b/server/src/client/web/VoiceBridge.cpp @@ -3,7 +3,6 @@ #include #include #include "WebClient.h" -#include "VoiceBridge.h" using namespace std; using namespace ts; @@ -162,10 +161,10 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr &undef } else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) { auto stream = dynamic_pointer_cast(undefined_stream); if(!stream) return; - this->_audio_channel = stream; + logTrace(this->server_id(), "Audio channel extensions:"); for(const auto& ex : stream->list_extensions()) { - debugMessage(0, "{} | {}", ex->name, ex->id); + logTrace(this->server_id(), " - {}: {}", ex->id, ex->name); } stream->register_local_extension("urn:ietf:params:rtp-hdrext:ssrc-audio-level"); @@ -175,7 +174,20 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr &undef break; } } - stream->incoming_data_handler = [&](const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { this->handle_audio_data(channel, data, payload_offset); }; + + 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 &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 &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()); } @@ -192,7 +204,7 @@ void VoiceBridge::handle_data_channel(const std::shared_ptr &c if(buffer.length() < 2) return; - this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */ + this->callback_voice_data(buffer.view(2), buffer[0] == 1); }; channel->callback_close = [&, weak_channel] { @@ -212,7 +224,7 @@ void VoiceBridge::handle_data_channel(const std::shared_ptr &c if(buffer.length() < 1) return; - this->callback_voice_whisper_data(buffer.view(2), buffer[0] == 1); + this->callback_voice_whisper_data(buffer.view(1), buffer[0] == 1); }; channel->callback_close = [&, weak_channel] { @@ -225,31 +237,67 @@ void VoiceBridge::handle_data_channel(const std::shared_ptr &c } } -void VoiceBridge::handle_audio_data(const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { +void VoiceBridge::handle_audio_voice_data(const std::shared_ptr &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); + //debugMessage(this->server_id(), "{} Got unknown codec ({})!", CLIENT_STR_LOG_PREFIX_(this->owner()), channel->codec->type); return; } - auto ac = _audio_channel.lock(); - if(!ac) return; + this->handle_audio_voice_x_data(&this->voice_state, data, payload_offset); +} - for(const auto& ext : ac->list_extensions(rtc::direction::incoming)) { +void VoiceBridge::handle_audio_voice_whisper_data(const std::shared_ptr &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) return; //Silence + if(level == 127) { + is_silence = true; + break; + } } break; } } - //int level; - //rtc::protocol::rtp_header_extension_parse_audio_level((char*) data.data(), data.length(), 1, &level); - auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3); - le2be16(this->voice.packet_id++, (char*) target_buffer.data_ptr()); - target_buffer[2] = 5; - memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset); - this->callback_voice_data(target_buffer, this->voice.packet_id < 7, false); + 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); + } } \ No newline at end of file diff --git a/server/src/client/web/VoiceBridge.h b/server/src/client/web/VoiceBridge.h index 314e07a..60de318 100644 --- a/server/src/client/web/VoiceBridge.h +++ b/server/src/client/web/VoiceBridge.h @@ -11,8 +11,7 @@ namespace ts { namespace web { class VoiceBridge { public: - typedef std::function cb_voice_data; - typedef std::function cb_voice_whisper_data; + typedef std::function cb_voice_data; typedef std::function cb_ice_candidate; typedef std::function cb_ice_candidate_finish; typedef std::function cb_initialized; @@ -33,12 +32,20 @@ namespace ts { cb_ice_candidate callback_ice_candidate; cb_ice_candidate_finish callback_ice_candidate_finished; cb_voice_data callback_voice_data; - cb_voice_whisper_data callback_voice_whisper_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& 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(); @@ -46,20 +53,30 @@ namespace ts { void handle_media_stream(const std::shared_ptr& /* stream */); void handle_data_channel(const std::shared_ptr & /* channel */); - void handle_audio_data(const std::shared_ptr& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */); + + void handle_audio_voice_data(const std::shared_ptr& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */); + void handle_audio_voice_whisper_data(const std::shared_ptr& /* 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 _owner; std::chrono::system_clock::time_point offer_timestamp; std::unique_ptr connection; - std::shared_ptr voice_channel_; - std::shared_ptr voice_whisper_channel_; + std::shared_ptr voice_channel_{}; + std::shared_ptr voice_whisper_channel_{}; - std::weak_ptr _audio_channel; - struct { - uint16_t packet_id = 0; - bool muted = true; - } voice; + std::weak_ptr incoming_voice_channel_{}; + std::weak_ptr 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 + }; }; } } \ No newline at end of file diff --git a/server/src/client/web/WSWebClient.cpp b/server/src/client/web/WSWebClient.cpp index 3cbf0a6..45efdf5 100644 --- a/server/src/client/web/WSWebClient.cpp +++ b/server/src/client/web/WSWebClient.cpp @@ -12,7 +12,7 @@ using namespace ts::protocol; void WebClient::handleMessageWrite(int fd, short, void *) { auto self_lock = _this.lock(); - unique_lock buffer_lock(this->queue_lock); + unique_lock buffer_lock(this->queue_mutex); if(this->queue_write.empty()) return; auto buffer = this->queue_write[0]; @@ -85,7 +85,7 @@ void WebClient::handleMessageRead(int fd, short, void *) { pbuffer.write(buffer, length); { - lock_guard lock(this->queue_lock); + lock_guard lock(this->queue_mutex); this->queue_read.push_back(std::move(pbuffer)); } @@ -95,7 +95,7 @@ void WebClient::handleMessageRead(int fd, short, void *) { void WebClient::enqueue_raw_packet(const pipes::buffer_view &msg) { auto buffer = msg.owns_buffer() ? msg.own_buffer() : msg.own_buffer(); /* TODO: Use buffer::allocate_buffer(...) */ { - lock_guard queue_lock(this->queue_lock); + lock_guard queue_lock(this->queue_mutex); this->queue_write.push_back(buffer); } { @@ -124,11 +124,11 @@ inline bool is_ssl_handshake_header(const pipes::buffer_view& buffer) { } void WebClient::processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */) { - lock_guard execute_lock(this->execute_lock); + lock_guard execute_lock(this->execute_mutex); if(this->state != ConnectionState::INIT_HIGH && this->state != ConnectionState::INIT_LOW && this->state != ConnectionState::CONNECTED) return; - unique_lock buffer_lock(this->queue_lock); + unique_lock buffer_lock(this->queue_mutex); if(this->queue_read.empty()) return; @@ -152,6 +152,7 @@ void WebClient::processNextMessage(const std::chrono::system_clock::time_point& this->ws_handler.process_incoming_data(buffer); } - if(has_next) + if(has_next) { this->registerMessageProcess(); + } } \ No newline at end of file diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index a39397c..e5588f6 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -220,7 +220,7 @@ bool WebClient::close_connection(const std::chrono::system_clock::time_point& ti flag_flushed = true; { - lock_guard lock(self_lock->queue_lock); + lock_guard lock(self_lock->queue_mutex); flag_flushed &= self_lock->queue_read.empty(); flag_flushed &= self_lock->queue_write.empty(); } @@ -265,6 +265,11 @@ command_result WebClient::handleCommand(Command &command) { return result; } } + if(command.command() == "setwhispertarget") { + return this->handleCommandSetWhisperTarget(command); + } else if(command.command() == "clearwhispertarget") { + return this->handleCommandClearWhisperTarget(command); + } return SpeakingClient::handleCommand(command); } @@ -333,7 +338,7 @@ void WebClient::onWSDisconnected(const string& error) { void WebClient::onWSMessage(const pipes::WSMessage &message) { if(message.code == pipes::OpCode::TEXT) - this->handleMessage(message.data.string()); + this->handleMessage(message.data); else if(message.code == pipes::OpCode::PING) { logTrace(this->getServerId(), "{} Received ping on web socket. Application data length: {}. Sending pong", CLIENT_STR_LOG_PREFIX, message.data.length()); this->ws_handler.send({pipes::PONG, message.data}); @@ -374,7 +379,7 @@ void WebClient::disconnectFinal() { auto self_lock = this->_this.lock(); { /* waiting to finish all executes */ - lock_guard lock(this->execute_lock); + lock_guard lock(this->execute_mutex); } if(this->flush_thread.get_id() == this_thread::get_id()) @@ -431,22 +436,24 @@ Json::CharReaderBuilder json_reader_builder = []() noexcept { return reader_builder; }(); -void WebClient::handleMessage(const std::string &message) { +void WebClient::handleMessage(const pipes::buffer_view &message) { /* Not really a need, this will directly be called via the ssl ws pipe, which has been triggered via progress message */ - threads::MutexLock lock(this->execute_lock); + threads::MutexLock lock(this->execute_mutex); Json::Value val; try { unique_ptr reader{json_reader_builder.newCharReader()}; string error_message; - if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) + if(!reader->parse(message.data_ptr(),message.data_ptr() + message.length(), &val, &error_message)) { throw Json::Exception("Could not parse payload! (" + error_message + ")"); + } } catch (const std::exception& ex) { - logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what())); - logTrace(this->server->getServerId(), "Payload: " + message); + logError(this->server->getServerId(), "Could not parse web message! Message: {}", std::string{ex.what()}); + logTrace(this->server->getServerId(), "Payload: {}", message.string()); return; } - logTrace(this->server->getServerId(), "[{}] Read message {}", CLIENT_STR_LOG_PREFIX_(this), message); + logTrace(this->server->getServerId(), "[{}] Read message {}", CLIENT_STR_LOG_PREFIX_(this), std::string_view{message.data_ptr(), message.length()}); + try { if(val["type"].isNull()) { logError(this->server->getServerId(), "[{}] Invalid web json package!"); @@ -478,15 +485,40 @@ void WebClient::handleMessage(const std::string &message) { this->voice_bridge = make_unique(dynamic_pointer_cast(this->ref())); //FIXME Add config - this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) { + 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, a, b); + this->handlePacketVoice(buffer, head, false); }; - this->voice_bridge->callback_voice_whisper_data = [&](const pipes::buffer_view& buffer, bool a) { + 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()); - this->handlePacketVoiceWhisper(buffer, a); + + 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() + 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); @@ -697,16 +729,116 @@ void WebClient::send_voice_packet(const pipes::buffer_view &view, const Speaking } } -void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) { - std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock); +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}; if(this->voice_bridge) { auto channel = this->voice_bridge->voice_whisper_channel(); if(channel) { - channel->send(view); + 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, view.length()); + this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, teaspeak_packet.length()); } } } + +command_result WebClient::handleCommandSetWhisperTarget(Command &command) { + auto server = this->getServer(); + if(!server) { + return command_result{error::server_unbound}; + } + + if(command.hasParm("new")) { + auto type = command["type"].as(); + auto target = command["target"].as(); + auto target_id = command["id"].as(); + + std::lock_guard whisper_buffer_lock{this->whisper.mutex}; + this->whisper.is_set = true; + this->whisper.is_new_header = true; + this->whisper.target_header.resize(10); + + this->whisper.target_header[0] = type; + this->whisper.target_header[1] = target; + le2be64(target_id, &this->whisper.target_header[2]); + + return command_result{error::ok}; + } else { + if(command.bulkCount() > 255) { + return command_result{error::parameter_invalid_count}; + } + + std::vector client_ids{}; + std::vector channel_ids{}; + + client_ids.reserve(command.bulkCount()); + channel_ids.reserve(command.bulkCount()); + + std::optionalgetClients())> server_clients{}; + + for(size_t bulk{0}; bulk < command.bulkCount(); bulk++) { + if(command[bulk].has("cid")) { + channel_ids.push_back(command[bulk]["cid"]); + } + + if(command[bulk].has("clid")) { + channel_ids.push_back(command[bulk]["clid"]); + } + + if(command[bulk].has("cluid")) { + auto client_unique_id = command[bulk]["cluid"].string(); + if(!server_clients.has_value()) { + server_clients = server->getClients(); + } + + for(const auto& client : *server_clients) { + if(client->getUid() == client_unique_id) { + client_ids.push_back(client->getClientId()); + } + } + } + } + + /* check if we're exceeding the protocol limit */ + if(client_ids.size() > 255) { + return command_result{error::whisper_too_many_targets}; + } + if(channel_ids.size() > 255) { + return command_result{error::whisper_too_many_targets}; + } + + /* generate the whisper target header */ + std::lock_guard whisper_buffer_lock{this->whisper.mutex}; + this->whisper.is_set = true; + this->whisper.is_new_header = false; + this->whisper.target_header.resize(client_ids.size() * 2 + channel_ids.size() * 8 + 2); + static_assert(sizeof(ChannelId) == 8); + static_assert(sizeof(ClientId) == 2); + + size_t offset{0}; + + this->whisper.target_header[0] = channel_ids.size(); + this->whisper.target_header[1] = client_ids.size(); + offset += 2; + + memcpy(this->whisper.target_header.data_ptr() + offset, channel_ids.data(), channel_ids.size() * 8); + offset += channel_ids.size() * 8; + + memcpy(this->whisper.target_header.data_ptr() + offset, client_ids.data(), client_ids.size() * 2); + //offset += channel_ids.size() * 2; + } + return command_result{error::ok}; +} + +command_result WebClient::handleCommandClearWhisperTarget(Command &command) { + std::lock_guard whisper_buffer_lock{this->whisper.mutex}; + this->whisper.is_set = false; + return command_result{error::ok}; +} \ No newline at end of file diff --git a/server/src/client/web/WebClient.h b/server/src/client/web/WebClient.h index 3d9f652..09a6c60 100644 --- a/server/src/client/web/WebClient.h +++ b/server/src/client/web/WebClient.h @@ -33,9 +33,9 @@ namespace ts::server { bool shouldReceiveVoice(const std::shared_ptr &sender) override; - inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); } - inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; } - inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; } + [[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_7() const { return this->js_ping.value; } protected: void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */ @@ -43,18 +43,23 @@ namespace ts::server { void applySelfLock(const std::shared_ptr &cl){ _this = cl; } private: WebControlServer* handle; - std::chrono::time_point connectedTimestamp; std::shared_mutex voice_bridge_lock; std::unique_ptr voice_bridge; int file_descriptor; + bool allow_raw_commands{false}; + bool ssl_detected{false}; + bool ssl_encrypted{true}; + pipes::SSL ssl_handler; + pipes::WebSocket ws_handler; + std::mutex event_lock; ::event* readEvent; ::event* writeEvent; struct { - uint8_t current_id = 0; + uint8_t current_id{0}; std::chrono::system_clock::time_point last_request; std::chrono::system_clock::time_point last_response; @@ -63,7 +68,7 @@ namespace ts::server { } ping; struct { - uint8_t current_id = 0; + uint8_t current_id{0}; std::chrono::system_clock::time_point last_request; std::chrono::system_clock::time_point last_response; @@ -71,21 +76,23 @@ namespace ts::server { std::chrono::nanoseconds timeout{2000}; } js_ping; - std::mutex queue_lock; + std::mutex queue_mutex; std::deque queue_read; std::deque queue_write; - threads::Mutex execute_lock; /* needs to be recursive! */ + threads::Mutex execute_mutex; /* needs to be recursive! */ std::thread flush_thread; std::recursive_mutex close_lock; + + struct { + std::mutex mutex{}; + pipes::buffer target_header{}; + bool is_new_header{false}; + bool is_set{false}; + } whisper; private: void initialize(); - bool allow_raw_commands{false}; - bool ssl_detected{false}; - bool ssl_encrypted{true}; - pipes::SSL ssl_handler; - pipes::WebSocket ws_handler; void handleMessageRead(int, short, void*); void handleMessageWrite(int, short, void*); void enqueue_raw_packet(const pipes::buffer_view& /* buffer */); @@ -101,16 +108,19 @@ namespace ts::server { void onWSMessage(const pipes::WSMessage&); protected: void disconnectFinal(); - void handleMessage(const std::string &); + void handleMessage(const pipes::buffer_view&); public: void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; - void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; + void send_voice_whisper_packet(const pipes::buffer_view &/* teamspeak packet */, const pipes::buffer_view &/* teaspeak packet */, const VoicePacketFlags &flags) override; protected: command_result handleCommand(Command &command) override; command_result handleCommandClientInit(Command &command) override; + + command_result handleCommandSetWhisperTarget(Command &command); + command_result handleCommandClearWhisperTarget(Command &command); }; } #endif \ No newline at end of file diff --git a/server/src/server/VoiceServer.cpp b/server/src/server/VoiceServer.cpp index ec730d2..3b0e83e 100644 --- a/server/src/server/VoiceServer.cpp +++ b/server/src/server/VoiceServer.cpp @@ -273,7 +273,7 @@ void VoiceServer::handleMessageRead(int fd, short events, void *_event_handle) { if((message.msg_flags & MSG_TRUNC) > 0) logError(ts_server->getServerId(), "Received truncated message from {}", net::to_string(remote_address)); - if(bytes_read < 0){ + if(bytes_read < 0) { if(errno == EAGAIN) break; //Nothing more to read diff --git a/shared b/shared index 4d7fabe..0a960e4 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 4d7fabe2eae09068e0c3bb47e1b0d5d08df11e45 +Subproject commit 0a960e414811bae5081b45219aad97f6cff5c512