Some more updates
This commit is contained in:
parent
7d0db0dea0
commit
4f5a4dc993
@ -1 +1 @@
|
|||||||
Subproject commit b83a798e5348cbf916b84c1ab684adbadbaf9a71
|
Subproject commit 8fbf120f756d603da38029843a8844a743440bac
|
@ -257,7 +257,7 @@ 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 "4")
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH "19")
|
SET(CPACK_PACKAGE_VERSION_PATCH "20")
|
||||||
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 "")
|
||||||
|
@ -20,6 +20,9 @@ using namespace ts::protocol;
|
|||||||
|
|
||||||
//#define PKT_LOG_VOICE
|
//#define PKT_LOG_VOICE
|
||||||
//#define PKT_LOG_WHISPER
|
//#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<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;
|
||||||
@ -150,42 +153,35 @@ inline bool update_whisper_error(std::chrono::system_clock::time_point& last) {
|
|||||||
//Server group => type := SERVER_GROUP and target_id := <server group id>
|
//Server group => type := SERVER_GROUP and target_id := <server group id>
|
||||||
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
|
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
|
||||||
//Channel commander => type := CHANNEL_COMMANDER and target_id := 0
|
//Channel commander => type := CHANNEL_COMMANDER and target_id := 0
|
||||||
void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bool new_packet) {
|
void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& payload, bool new_packet, bool head) {
|
||||||
if(data.length() < 5) {
|
if(payload.length() < 5) {
|
||||||
this->disconnect("Invalid packet (Voice whisper)");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t offset = 0;
|
uint16_t payload_offset{0};
|
||||||
auto vpacketId = be2le16((char*) data.data_ptr(), offset, &offset);
|
auto voice_packet_id = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset);
|
||||||
auto codec = (uint8_t) data[offset++];
|
auto voice_codec = (uint8_t) payload[payload_offset++];
|
||||||
|
|
||||||
VoicePacketFlags flags{};
|
std::deque<std::shared_ptr<SpeakingClient>> target_clients;
|
||||||
flags.head = false;
|
|
||||||
flags.fragmented = false;
|
|
||||||
flags.new_protocol = new_packet;
|
|
||||||
|
|
||||||
if(new_packet) {
|
if(new_packet) {
|
||||||
if(data.length() < 7) {
|
if(payload.length() < 7) {
|
||||||
this->disconnect("Invalid packet (Voice whisper | New)");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = (WhisperType) data[offset++];
|
auto type = (WhisperType) payload[payload_offset++];
|
||||||
auto target = (WhisperTarget) data[offset++];
|
auto target = (WhisperTarget) payload[payload_offset++];
|
||||||
auto type_id = be2le64((char*) data.data_ptr(), offset, &offset);
|
auto type_id = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset);
|
||||||
this->resetIdleTime();
|
|
||||||
|
|
||||||
size_t data_length = data.length() - offset;
|
|
||||||
#ifdef PKT_LOG_WHISPER
|
#ifdef PKT_LOG_WHISPER
|
||||||
logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id);
|
logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
deque<shared_ptr<SpeakingClient>> available_clients;
|
|
||||||
if(type == WhisperType::ECHO) {
|
if(type == WhisperType::ECHO) {
|
||||||
available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
target_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
||||||
} else {
|
} else {
|
||||||
for(const auto& client : this->server->getClients()) {
|
for(const auto& client : this->server->getClients()) {
|
||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(client);
|
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(client);
|
||||||
@ -193,84 +189,82 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
|
|||||||
if(!speakingClient->currentChannel) continue;
|
if(!speakingClient->currentChannel) continue;
|
||||||
|
|
||||||
if(type == WhisperType::ALL) {
|
if(type == WhisperType::ALL) {
|
||||||
available_clients.push_back(speakingClient);
|
target_clients.push_back(speakingClient);
|
||||||
} else if(type == WhisperType::SERVER_GROUP) {
|
} else if(type == WhisperType::SERVER_GROUP) {
|
||||||
if(type_id == 0)
|
if(type_id == 0)
|
||||||
available_clients.push_back(speakingClient);
|
target_clients.push_back(speakingClient);
|
||||||
else {
|
else {
|
||||||
shared_lock client_lock(this->channel_lock);
|
shared_lock client_lock(this->channel_lock);
|
||||||
for(const auto& id : client->cached_server_groups) {
|
for(const auto& id : client->cached_server_groups) {
|
||||||
if(id == type_id) {
|
if(id == type_id) {
|
||||||
available_clients.push_back(speakingClient);
|
target_clients.push_back(speakingClient);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(type == WhisperType::CHANNEL_GROUP) {
|
} else if(type == WhisperType::CHANNEL_GROUP) {
|
||||||
if(client->cached_channel_group == type_id)
|
if(client->cached_channel_group == type_id)
|
||||||
available_clients.push_back(speakingClient);
|
target_clients.push_back(speakingClient);
|
||||||
} else if(type == WhisperType::CHANNEL_COMMANDER) {
|
} else if(type == WhisperType::CHANNEL_COMMANDER) {
|
||||||
if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as<bool>())
|
if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as<bool>())
|
||||||
available_clients.push_back(speakingClient);
|
target_clients.push_back(speakingClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(target == WhisperTarget::CHANNEL_CURRENT) {
|
if(target == WhisperTarget::CHANNEL_CURRENT) {
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
return target->currentChannel != this->currentChannel;
|
return target->currentChannel != this->currentChannel;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
} else if(target == WhisperTarget::CHANNEL_PARENT) {
|
} else if(target == WhisperTarget::CHANNEL_PARENT) {
|
||||||
auto current_parent = this->currentChannel->parent();
|
auto current_parent = this->currentChannel->parent();
|
||||||
if(!current_parent) return;
|
if(!current_parent) return;
|
||||||
|
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
return target->currentChannel != current_parent;
|
return target->currentChannel != current_parent;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
} else if(target == WhisperTarget::CHANNEL_ALL_PARENT) {
|
} else if(target == WhisperTarget::CHANNEL_ALL_PARENT) {
|
||||||
shared_ptr<BasicChannel> current_parent;
|
shared_ptr<BasicChannel> current_parent;
|
||||||
{
|
{
|
||||||
current_parent = this->currentChannel->parent();
|
current_parent = this->currentChannel->parent();
|
||||||
if(!current_parent) return;
|
if(!current_parent) return;
|
||||||
}
|
}
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
auto tmp_parent = current_parent;
|
auto tmp_parent = current_parent;
|
||||||
while(tmp_parent && tmp_parent != target->currentChannel)
|
while(tmp_parent && tmp_parent != target->currentChannel)
|
||||||
tmp_parent = tmp_parent->parent();
|
tmp_parent = tmp_parent->parent();
|
||||||
return target->currentChannel != tmp_parent;
|
return target->currentChannel != tmp_parent;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
} else if(target == WhisperTarget::CHANNEL_FAMILY) {
|
} else if(target == WhisperTarget::CHANNEL_FAMILY) {
|
||||||
shared_ptr<BasicChannel> current = this->currentChannel;
|
shared_ptr<BasicChannel> current = this->currentChannel;
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
auto tmp_current = target->currentChannel;
|
auto tmp_current = target->currentChannel;
|
||||||
while(tmp_current && tmp_current != current)
|
while(tmp_current && tmp_current != current)
|
||||||
tmp_current = tmp_current->parent();
|
tmp_current = tmp_current->parent();
|
||||||
return current != tmp_current;
|
return current != tmp_current;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
} else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) {
|
} else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) {
|
||||||
shared_ptr<BasicChannel> current = this->currentChannel;
|
shared_ptr<BasicChannel> current = this->currentChannel;
|
||||||
while(current && current->parent()) current = current->parent();
|
while(current && current->parent()) current = current->parent();
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
auto tmp_current = target->currentChannel;
|
auto tmp_current = target->currentChannel;
|
||||||
while(tmp_current && tmp_current != current)
|
while(tmp_current && tmp_current != current)
|
||||||
tmp_current = tmp_current->parent();
|
tmp_current = tmp_current->parent();
|
||||||
return current != tmp_current;
|
return current != tmp_current;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
} else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) {
|
} else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) {
|
||||||
shared_ptr<BasicChannel> current = this->currentChannel;
|
shared_ptr<BasicChannel> current = this->currentChannel;
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
|
||||||
return target->currentChannel->parent() != current;
|
return target->currentChannel->parent() != current;
|
||||||
}), available_clients.end());
|
}), target_clients.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto self_lock = this->_this.lock();
|
auto self_lock = this->_this.lock();
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
|
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
|
||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
|
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
|
||||||
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
|
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)) {
|
if(update_whisper_error(this->speak_last_no_whisper_target)) {
|
||||||
command_result result{error::whisper_no_targets};
|
command_result result{error::whisper_no_targets};
|
||||||
this->notifyError(result);
|
this->notifyError(result);
|
||||||
@ -278,7 +272,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
|
if(target_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
|
||||||
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
|
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
|
||||||
command_result result{error::whisper_too_many_targets};
|
command_result result{error::whisper_too_many_targets};
|
||||||
this->notifyError(result);
|
this->notifyError(result);
|
||||||
@ -286,103 +280,143 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
|
|||||||
return;
|
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 {
|
} else {
|
||||||
auto clientCount = (uint8_t) data[offset++];
|
auto channelCount = (uint8_t) payload[payload_offset++];
|
||||||
auto channelCount = (uint8_t) data[offset++];
|
auto clientCount = (uint8_t) payload[payload_offset++];
|
||||||
if(data.length() < 5 + channelCount * 2 + clientCount * 8) {
|
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, data.length(), to_string(5 + channelCount * 2 + clientCount * 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->resetIdleTime();
|
ChannelId channelIds[channelCount];
|
||||||
ChannelId channelIds[clientCount];
|
ClientId clientIds[clientCount];
|
||||||
ClientId clientIds[channelCount];
|
|
||||||
|
|
||||||
for(uint8_t index = 0; index < clientCount; index++)
|
for(uint8_t index = 0; index < channelCount; index++) {
|
||||||
channelIds[index] = be2le64((char*) data.data_ptr(), offset, &offset);
|
channelIds[index] = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset);
|
||||||
for(uint8_t index = 0; index < channelCount; index++)
|
}
|
||||||
clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset);
|
|
||||||
|
for(uint8_t index = 0; index < clientCount; index++) {
|
||||||
auto available_clients = this->server->getClients();
|
clientIds[index] = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset);
|
||||||
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
|
|
||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(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<ConnectedClient>& cl) {
|
|
||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(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<size_t>()) {
|
|
||||||
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
|
|
||||||
command_result result{error::whisper_too_many_targets};
|
|
||||||
this->notifyError(result);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t dataLength = data.length() - offset;
|
|
||||||
#ifdef PKT_LOG_WHISPER
|
#ifdef PKT_LOG_WHISPER
|
||||||
logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount);
|
logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount);
|
||||||
#endif
|
#endif
|
||||||
//Create the packet data
|
|
||||||
char packetBuffer[OUT_WHISPER_PKT_OFFSET + dataLength];
|
|
||||||
if(offset < data.length())
|
|
||||||
memcpy(&packetBuffer[OUT_WHISPER_PKT_OFFSET], &data[offset], dataLength);
|
|
||||||
|
|
||||||
le2be16(vpacketId, packetBuffer, 0);
|
for(const auto& client : this->server->getClients()) {
|
||||||
le2be16(this->getClientId(), packetBuffer, 2);
|
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(client);
|
||||||
packetBuffer[4] = codec;
|
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<size_t>()) {
|
||||||
|
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 whisper_packet_buffer[kWhisperMaxHeaderLength + voice_payload_length];
|
||||||
|
size_t whisper_packet_offset{0};
|
||||||
|
size_t whisper_packet_teamspeak_offset{0};
|
||||||
|
|
||||||
|
/* writing the teaspeak header */
|
||||||
|
if(head) {
|
||||||
|
auto uniqueId = this->getUid();
|
||||||
|
auto nickname = this->getDisplayName();
|
||||||
|
|
||||||
|
if(uniqueId.length() > kWhisperClientUniqueIdLength) {
|
||||||
|
logCritical(LOG_GENERAL, "Clients unique id is longer than the expected max length of {}. Unique length: {}", kWhisperClientUniqueIdLength, uniqueId.length());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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{};
|
VoicePacketFlags flags{};
|
||||||
auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength);
|
flags.head = head;
|
||||||
|
|
||||||
for(const auto& cl : available_clients){ //Faster?
|
pipes::buffer_view teaspeak_packet{}, teamspeak_packet{};
|
||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
|
teaspeak_packet = pipes::buffer_view{whisper_packet_buffer, whisper_packet_offset};
|
||||||
assert(speakingClient);
|
teamspeak_packet = pipes::buffer_view{whisper_packet_buffer + whisper_packet_teamspeak_offset, whisper_packet_offset - whisper_packet_teamspeak_offset};
|
||||||
if(speakingClient->shouldReceiveVoiceWhisper(_this.lock()))
|
|
||||||
speakingClient->send_voice_whisper_packet(data, flags);
|
for(const auto& cl : target_clients) {
|
||||||
|
if(cl->shouldReceiveVoiceWhisper(_this.lock())) {
|
||||||
|
cl->send_voice_whisper_packet(teamspeak_packet, teaspeak_packet, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->updateSpeak(false, system_clock::now());
|
this->resetIdleTime();
|
||||||
}
|
this->updateSpeak(false, std::chrono::system_clock::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEST_PARM(type) \
|
#define TEST_PARM(type) \
|
||||||
|
@ -43,7 +43,7 @@ namespace ts::server {
|
|||||||
|
|
||||||
//Whisper
|
//Whisper
|
||||||
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender);
|
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &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() {
|
inline std::chrono::milliseconds takeSpokenTime() {
|
||||||
auto time = this->speak_time;
|
auto time = this->speak_time;
|
||||||
@ -62,7 +62,7 @@ namespace ts::server {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
|
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 processJoin();
|
||||||
void processLeave();
|
void processLeave();
|
||||||
|
@ -896,7 +896,7 @@ command_result ConnectedClient::handleCommandClientGetUidFromClid(Command &cmd)
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
auto client_list = this->server->getClients();
|
auto client_list = this->server->getClients();
|
||||||
|
|
||||||
Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetuidfromclid" : "");
|
Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientuidfromclid" : "");
|
||||||
int result_index = 0;
|
int result_index = 0;
|
||||||
|
|
||||||
for(int index = 0; index < cmd.bulkCount(); index++) {
|
for(int index = 0; index < cmd.bulkCount(); index++) {
|
||||||
|
@ -2819,6 +2819,7 @@ command_result ConnectedClient::handleCommandListFeatureSupport(ts::Command &cmd
|
|||||||
REGISTER_FEATURE("error-bulks", FeatureSupportMode::FULL, 1);
|
REGISTER_FEATURE("error-bulks", FeatureSupportMode::FULL, 1);
|
||||||
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);
|
||||||
|
|
||||||
this->sendCommand(notify);
|
this->sendCommand(notify);
|
||||||
return command_result{error::ok};
|
return command_result{error::ok};
|
||||||
|
@ -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<void>(), voice_buffer.length());
|
this->connection->send_packet(PacketType::VOICE, packet_flags, voice_buffer.data_ptr<void>(), 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};
|
PacketFlag::PacketFlags packet_flags{PacketFlag::None};
|
||||||
packet_flags |= flags.encrypted ? 0U : PacketFlag::Unencrypted;
|
packet_flags |= flags.encrypted ? 0U : PacketFlag::Unencrypted;
|
||||||
packet_flags |= flags.head ? PacketFlag::Compressed : 0U;
|
packet_flags |= flags.head ? PacketFlag::Compressed : 0U;
|
||||||
packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0U;
|
packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0U;
|
||||||
packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0U;
|
packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0U;
|
||||||
|
|
||||||
this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, voice_buffer.data_ptr<void>(), voice_buffer.length());
|
if(this->getType() == ClientType::CLIENT_TEASPEAK) {
|
||||||
|
this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teaspeak_packet.data_ptr<void>(), teaspeak_packet.length());
|
||||||
|
} else {
|
||||||
|
this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teamspeak_packet.data_ptr<void>(), teamspeak_packet.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float VoiceClient::current_ping_deviation() {
|
float VoiceClient::current_ping_deviation() {
|
||||||
|
@ -90,7 +90,11 @@ namespace ts {
|
|||||||
void handlePacketCommand(const pipes::buffer_view&);
|
void handlePacketCommand(const pipes::buffer_view&);
|
||||||
public:
|
public:
|
||||||
void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override;
|
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:
|
protected:
|
||||||
virtual command_result handleCommand(Command &command) override;
|
virtual command_result handleCommand(Command &command) override;
|
||||||
|
@ -35,7 +35,7 @@ void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientP
|
|||||||
auto client = this->getCurrentClient();
|
auto client = this->getCurrentClient();
|
||||||
if(!client) return;
|
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) {
|
void VoiceClientConnection::handlePacketAck(const protocol::ClientPacketParser& packet) {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <misc/endianness.h>
|
#include <misc/endianness.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include "WebClient.h"
|
#include "WebClient.h"
|
||||||
#include "VoiceBridge.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ts;
|
using namespace ts;
|
||||||
@ -162,10 +161,10 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr<rtc::Channel> &undef
|
|||||||
} else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) {
|
} else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) {
|
||||||
auto stream = dynamic_pointer_cast<rtc::AudioChannel>(undefined_stream);
|
auto stream = dynamic_pointer_cast<rtc::AudioChannel>(undefined_stream);
|
||||||
if(!stream) return;
|
if(!stream) return;
|
||||||
this->_audio_channel = stream;
|
|
||||||
|
|
||||||
|
logTrace(this->server_id(), "Audio channel extensions:");
|
||||||
for(const auto& ex : stream->list_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");
|
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<rtc::Channel> &undef
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream->incoming_data_handler = [&](const std::shared_ptr<rtc::MediaChannel> &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<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 {
|
} else {
|
||||||
logError(this->server_id(), "Got offer for unknown channel of type {}", undefined_stream->type());
|
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<rtc::DataChannel> &c
|
|||||||
if(buffer.length() < 2)
|
if(buffer.length() < 2)
|
||||||
return;
|
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] {
|
channel->callback_close = [&, weak_channel] {
|
||||||
@ -212,7 +224,7 @@ void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &c
|
|||||||
if(buffer.length() < 1)
|
if(buffer.length() < 1)
|
||||||
return;
|
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] {
|
channel->callback_close = [&, weak_channel] {
|
||||||
@ -225,31 +237,67 @@ void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceBridge::handle_audio_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ac = _audio_channel.lock();
|
this->handle_audio_voice_x_data(&this->voice_state, data, payload_offset);
|
||||||
if(!ac) return;
|
}
|
||||||
|
|
||||||
for(const auto& ext : ac->list_extensions(rtc::direction::incoming)) {
|
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") {
|
if(ext->name == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") {
|
||||||
int level;
|
int level;
|
||||||
if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) {
|
if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) {
|
||||||
//debugMessage(this->server_id(), "Audio level: {}", level);
|
//debugMessage(this->server_id(), "Audio level: {}", level);
|
||||||
if(level == 127) return; //Silence
|
if(level == 127) {
|
||||||
|
is_silence = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//int level;
|
|
||||||
//rtc::protocol::rtp_header_extension_parse_audio_level((char*) data.data(), data.length(), 1, &level);
|
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);
|
auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3);
|
||||||
le2be16(this->voice.packet_id++, (char*) target_buffer.data_ptr());
|
le2be16(state->sequence_packet_id++, (char*) target_buffer.data_ptr());
|
||||||
target_buffer[2] = 5;
|
target_buffer[2] = 5;
|
||||||
memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset);
|
memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset);
|
||||||
|
|
||||||
this->callback_voice_data(target_buffer, this->voice.packet_id < 7, false);
|
state->callback(target_buffer, state->sequence_packet_id < 7);
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,8 +11,7 @@ namespace ts {
|
|||||||
namespace web {
|
namespace web {
|
||||||
class VoiceBridge {
|
class VoiceBridge {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(const pipes::buffer_view&, bool, bool)> cb_voice_data;
|
typedef std::function<void(const pipes::buffer_view&, bool /* is sequence start */)> cb_voice_data;
|
||||||
typedef std::function<void(const pipes::buffer_view&, bool)> cb_voice_whisper_data;
|
|
||||||
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
|
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(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
|
||||||
typedef std::function<void()> cb_initialized;
|
typedef std::function<void()> cb_initialized;
|
||||||
@ -33,12 +32,20 @@ namespace ts {
|
|||||||
cb_ice_candidate callback_ice_candidate;
|
cb_ice_candidate callback_ice_candidate;
|
||||||
cb_ice_candidate_finish callback_ice_candidate_finished;
|
cb_ice_candidate_finish callback_ice_candidate_finished;
|
||||||
cb_voice_data callback_voice_data;
|
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_initialized callback_initialized;
|
||||||
cb_failed callback_failed;
|
cb_failed callback_failed;
|
||||||
|
|
||||||
void execute_tick();
|
void execute_tick();
|
||||||
private:
|
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, ...);
|
static void callback_log(void* ptr, pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...);
|
||||||
|
|
||||||
inline int server_id();
|
inline int server_id();
|
||||||
@ -46,20 +53,30 @@ namespace ts {
|
|||||||
|
|
||||||
void handle_media_stream(const std::shared_ptr<rtc::Channel>& /* stream */);
|
void handle_media_stream(const std::shared_ptr<rtc::Channel>& /* stream */);
|
||||||
void handle_data_channel(const std::shared_ptr<rtc::DataChannel> & /* channel */);
|
void handle_data_channel(const std::shared_ptr<rtc::DataChannel> & /* channel */);
|
||||||
void handle_audio_data(const std::shared_ptr<rtc::MediaChannel>& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
|
|
||||||
|
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::weak_ptr<server::WebClient> _owner;
|
||||||
std::chrono::system_clock::time_point offer_timestamp;
|
std::chrono::system_clock::time_point offer_timestamp;
|
||||||
std::unique_ptr<rtc::PeerConnection> connection;
|
std::unique_ptr<rtc::PeerConnection> connection;
|
||||||
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_channel_;
|
std::shared_ptr<rtc::DataChannel> voice_channel_{};
|
||||||
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_;
|
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_{};
|
||||||
|
|
||||||
std::weak_ptr<rtc::AudioChannel> _audio_channel;
|
std::weak_ptr<rtc::AudioChannel> incoming_voice_channel_{};
|
||||||
struct {
|
std::weak_ptr<rtc::AudioChannel> incoming_whisper_channel_{};
|
||||||
uint16_t packet_id = 0;
|
|
||||||
bool muted = true;
|
VoiceStateData voice_state{
|
||||||
} voice;
|
.channel = this->incoming_voice_channel_,
|
||||||
|
.callback = this->callback_voice_data
|
||||||
|
};
|
||||||
|
VoiceStateData whisper_state{
|
||||||
|
.channel = this->incoming_whisper_channel_,
|
||||||
|
.callback = this->callback_voice_whisper_data
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ using namespace ts::protocol;
|
|||||||
void WebClient::handleMessageWrite(int fd, short, void *) {
|
void WebClient::handleMessageWrite(int fd, short, void *) {
|
||||||
auto self_lock = _this.lock();
|
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;
|
if(this->queue_write.empty()) return;
|
||||||
|
|
||||||
auto buffer = this->queue_write[0];
|
auto buffer = this->queue_write[0];
|
||||||
@ -85,7 +85,7 @@ void WebClient::handleMessageRead(int fd, short, void *) {
|
|||||||
pbuffer.write(buffer, length);
|
pbuffer.write(buffer, length);
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard lock(this->queue_lock);
|
lock_guard lock(this->queue_mutex);
|
||||||
this->queue_read.push_back(std::move(pbuffer));
|
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) {
|
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(...) */
|
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);
|
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 */) {
|
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)
|
if(this->state != ConnectionState::INIT_HIGH && this->state != ConnectionState::INIT_LOW && this->state != ConnectionState::CONNECTED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unique_lock buffer_lock(this->queue_lock);
|
unique_lock buffer_lock(this->queue_mutex);
|
||||||
if(this->queue_read.empty())
|
if(this->queue_read.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -152,6 +152,7 @@ void WebClient::processNextMessage(const std::chrono::system_clock::time_point&
|
|||||||
this->ws_handler.process_incoming_data(buffer);
|
this->ws_handler.process_incoming_data(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(has_next)
|
if(has_next) {
|
||||||
this->registerMessageProcess();
|
this->registerMessageProcess();
|
||||||
}
|
}
|
||||||
|
}
|
@ -220,7 +220,7 @@ bool WebClient::close_connection(const std::chrono::system_clock::time_point& ti
|
|||||||
flag_flushed = true;
|
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_read.empty();
|
||||||
flag_flushed &= self_lock->queue_write.empty();
|
flag_flushed &= self_lock->queue_write.empty();
|
||||||
}
|
}
|
||||||
@ -265,6 +265,11 @@ command_result WebClient::handleCommand(Command &command) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(command.command() == "setwhispertarget") {
|
||||||
|
return this->handleCommandSetWhisperTarget(command);
|
||||||
|
} else if(command.command() == "clearwhispertarget") {
|
||||||
|
return this->handleCommandClearWhisperTarget(command);
|
||||||
|
}
|
||||||
return SpeakingClient::handleCommand(command);
|
return SpeakingClient::handleCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +338,7 @@ void WebClient::onWSDisconnected(const string& error) {
|
|||||||
|
|
||||||
void WebClient::onWSMessage(const pipes::WSMessage &message) {
|
void WebClient::onWSMessage(const pipes::WSMessage &message) {
|
||||||
if(message.code == pipes::OpCode::TEXT)
|
if(message.code == pipes::OpCode::TEXT)
|
||||||
this->handleMessage(message.data.string());
|
this->handleMessage(message.data);
|
||||||
else if(message.code == pipes::OpCode::PING) {
|
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());
|
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});
|
this->ws_handler.send({pipes::PONG, message.data});
|
||||||
@ -374,7 +379,7 @@ void WebClient::disconnectFinal() {
|
|||||||
auto self_lock = this->_this.lock();
|
auto self_lock = this->_this.lock();
|
||||||
{
|
{
|
||||||
/* waiting to finish all executes */
|
/* 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())
|
if(this->flush_thread.get_id() == this_thread::get_id())
|
||||||
@ -431,22 +436,24 @@ Json::CharReaderBuilder json_reader_builder = []() noexcept {
|
|||||||
return reader_builder;
|
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 */
|
/* 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;
|
Json::Value val;
|
||||||
try {
|
try {
|
||||||
unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()};
|
unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()};
|
||||||
|
|
||||||
string error_message;
|
string error_message;
|
||||||
if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message))
|
if(!reader->parse(message.data_ptr<char>(),message.data_ptr<char>() + message.length(), &val, &error_message)) {
|
||||||
throw Json::Exception("Could not parse payload! (" + error_message + ")");
|
throw Json::Exception("Could not parse payload! (" + error_message + ")");
|
||||||
|
}
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what()));
|
logError(this->server->getServerId(), "Could not parse web message! Message: {}", std::string{ex.what()});
|
||||||
logTrace(this->server->getServerId(), "Payload: " + message);
|
logTrace(this->server->getServerId(), "Payload: {}", message.string());
|
||||||
return;
|
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<char>(), message.length()});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(val["type"].isNull()) {
|
if(val["type"].isNull()) {
|
||||||
logError(this->server->getServerId(), "[{}] Invalid web json package!");
|
logError(this->server->getServerId(), "[{}] Invalid web json package!");
|
||||||
@ -478,15 +485,40 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
|
|
||||||
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(this->ref())); //FIXME Add config
|
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 a, bool b) {
|
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool head) {
|
||||||
/* may somehow get the "real" packet size? */
|
/* may somehow get the "real" packet size? */
|
||||||
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
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? */
|
/* may somehow get the "real" packet size? */
|
||||||
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
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<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 = [&](){
|
this->voice_bridge->callback_initialized = [&](){
|
||||||
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX);
|
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) {
|
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);
|
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
|
||||||
if(this->voice_bridge) {
|
if(this->voice_bridge) {
|
||||||
auto channel = this->voice_bridge->voice_whisper_channel();
|
auto channel = this->voice_bridge->voice_whisper_channel();
|
||||||
if(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();
|
read_voice_bridge_lock.unlock();
|
||||||
|
|
||||||
/* may somehow get the "real" packet size? */
|
/* 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<uint8_t>();
|
||||||
|
auto target = command["target"].as<uint8_t>();
|
||||||
|
auto target_id = command["id"].as<uint64_t>();
|
||||||
|
|
||||||
|
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<ClientId> client_ids{};
|
||||||
|
std::vector<ChannelId> channel_ids{};
|
||||||
|
|
||||||
|
client_ids.reserve(command.bulkCount());
|
||||||
|
channel_ids.reserve(command.bulkCount());
|
||||||
|
|
||||||
|
std::optional<decltype(server->getClients())> 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<char>() + offset, channel_ids.data(), channel_ids.size() * 8);
|
||||||
|
offset += channel_ids.size() * 8;
|
||||||
|
|
||||||
|
memcpy(this->whisper.target_header.data_ptr<char>() + 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};
|
||||||
|
}
|
@ -33,9 +33,9 @@ namespace ts::server {
|
|||||||
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
|
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
|
||||||
|
|
||||||
|
|
||||||
inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); }
|
[[nodiscard]] inline std::chrono::nanoseconds client_ping() const { return this->client_ping_layer_7(); }
|
||||||
inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; }
|
[[nodiscard]] inline std::chrono::nanoseconds client_ping_layer_5() const { return this->ping.value; }
|
||||||
inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; }
|
[[nodiscard]] inline std::chrono::nanoseconds client_ping_layer_7() const { return this->js_ping.value; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
|
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<WebClient> &cl){ _this = cl; }
|
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
|
||||||
private:
|
private:
|
||||||
WebControlServer* handle;
|
WebControlServer* handle;
|
||||||
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
|
||||||
|
|
||||||
std::shared_mutex voice_bridge_lock;
|
std::shared_mutex voice_bridge_lock;
|
||||||
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
||||||
int file_descriptor;
|
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;
|
std::mutex event_lock;
|
||||||
::event* readEvent;
|
::event* readEvent;
|
||||||
::event* writeEvent;
|
::event* writeEvent;
|
||||||
|
|
||||||
struct {
|
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_request;
|
||||||
std::chrono::system_clock::time_point last_response;
|
std::chrono::system_clock::time_point last_response;
|
||||||
|
|
||||||
@ -63,7 +68,7 @@ namespace ts::server {
|
|||||||
} ping;
|
} ping;
|
||||||
|
|
||||||
struct {
|
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_request;
|
||||||
std::chrono::system_clock::time_point last_response;
|
std::chrono::system_clock::time_point last_response;
|
||||||
|
|
||||||
@ -71,21 +76,23 @@ namespace ts::server {
|
|||||||
std::chrono::nanoseconds timeout{2000};
|
std::chrono::nanoseconds timeout{2000};
|
||||||
} js_ping;
|
} js_ping;
|
||||||
|
|
||||||
std::mutex queue_lock;
|
std::mutex queue_mutex;
|
||||||
std::deque<pipes::buffer> queue_read;
|
std::deque<pipes::buffer> queue_read;
|
||||||
std::deque<pipes::buffer> queue_write;
|
std::deque<pipes::buffer> queue_write;
|
||||||
threads::Mutex execute_lock; /* needs to be recursive! */
|
threads::Mutex execute_mutex; /* needs to be recursive! */
|
||||||
|
|
||||||
std::thread flush_thread;
|
std::thread flush_thread;
|
||||||
std::recursive_mutex close_lock;
|
std::recursive_mutex close_lock;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::mutex mutex{};
|
||||||
|
pipes::buffer target_header{};
|
||||||
|
bool is_new_header{false};
|
||||||
|
bool is_set{false};
|
||||||
|
} whisper;
|
||||||
private:
|
private:
|
||||||
void initialize();
|
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 handleMessageRead(int, short, void*);
|
||||||
void handleMessageWrite(int, short, void*);
|
void handleMessageWrite(int, short, void*);
|
||||||
void enqueue_raw_packet(const pipes::buffer_view& /* buffer */);
|
void enqueue_raw_packet(const pipes::buffer_view& /* buffer */);
|
||||||
@ -101,16 +108,19 @@ namespace ts::server {
|
|||||||
void onWSMessage(const pipes::WSMessage&);
|
void onWSMessage(const pipes::WSMessage&);
|
||||||
protected:
|
protected:
|
||||||
void disconnectFinal();
|
void disconnectFinal();
|
||||||
void handleMessage(const std::string &);
|
void handleMessage(const pipes::buffer_view&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
|
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:
|
protected:
|
||||||
|
|
||||||
command_result handleCommand(Command &command) override;
|
command_result handleCommand(Command &command) override;
|
||||||
command_result handleCommandClientInit(Command &command) override;
|
command_result handleCommandClientInit(Command &command) override;
|
||||||
|
|
||||||
|
command_result handleCommandSetWhisperTarget(Command &command);
|
||||||
|
command_result handleCommandClearWhisperTarget(Command &command);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
2
shared
2
shared
@ -1 +1 @@
|
|||||||
Subproject commit 4d7fabe2eae09068e0c3bb47e1b0d5d08df11e45
|
Subproject commit 0a960e414811bae5081b45219aad97f6cff5c512
|
Loading…
x
Reference in New Issue
Block a user