diff --git a/git-teaspeak b/git-teaspeak index d4ffb41..136fac9 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit d4ffb41adc2fe5145be4ab343039e72f66058d33 +Subproject commit 136fac9ad2a22bf39327f77cd59f2f83a02b2eec diff --git a/license/shared/src/client.cpp b/license/shared/src/client.cpp index 33e7169..2df935b 100644 --- a/license/shared/src/client.cpp +++ b/license/shared/src/client.cpp @@ -43,14 +43,14 @@ LicenseServerClient::LicenseServerClient(const sockaddr_in &address, int pversio } LicenseServerClient::~LicenseServerClient() { - this->close_connection(); + { + std::unique_lock slock{this->connection_lock}; + this->connection_state = ConnectionState::UNCONNECTED; + } + this->cleanup_network_resources(); /* force cleanup ignoring the previous state */ if(this->buffers.read) Buffer::free(this->buffers.read); - - const auto is_event_loop = this->network.event_dispatch.get_id() == std::this_thread::get_id(); - if(is_event_loop) this->network.event_dispatch.detach(); - else this->network.event_dispatch.join(); } bool LicenseServerClient::start_connection(std::string &error) { diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 8e9da8c..66948d2 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -49,6 +49,7 @@ set(SERVER_SOURCE_FILES src/client/voice/VoiceClientCommandHandler.cpp src/client/voice/VoiceClientPacketHandler.cpp src/client/voice/VoiceClientView.cpp + src/client/voice/PacketStatistics.cpp src/TS3ServerClientManager.cpp src/VirtualServer.cpp src/TS3ServerHeartbeat.cpp @@ -235,7 +236,7 @@ target_link_libraries(PermMapHelper SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MINOR "4") -SET(CPACK_PACKAGE_VERSION_PATCH "11") +SET(CPACK_PACKAGE_VERSION_PATCH "12") if (BUILD_TYPE_NAME EQUAL OFF) SET(CPACK_PACKAGE_VERSION_DATA "beta") elseif (BUILD_TYPE_NAME STREQUAL "") diff --git a/server/main.cpp b/server/main.cpp index 887c8f0..885750e 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -129,7 +129,6 @@ int main(int argc, char** argv) { terminal::install(); if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; } } - assert(ts::property::impl::validateUnique()); if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) { #define HELP_FMT " {} {} | {}" diff --git a/server/src/ConnectionStatistics.cpp b/server/src/ConnectionStatistics.cpp index 16b139f..2f56e76 100644 --- a/server/src/ConnectionStatistics.cpp +++ b/server/src/ConnectionStatistics.cpp @@ -3,8 +3,9 @@ // #include + +#include #include "ConnectionStatistics.h" -#include "VirtualServer.h" using namespace std; using namespace std::chrono; @@ -13,321 +14,98 @@ using namespace ts::server; using namespace ts::stats; using namespace ts::protocol; -ConnectionStatistics::ConnectionStatistics(const shared_ptr& handle, bool properties) : handle(handle) { +ConnectionStatistics::ConnectionStatistics(shared_ptr handle) : handle(std::move(handle)) { memtrack::allocated(this); - - if(properties) { - this->properties = make_shared(); //TODO load etc? - this->properties->register_property_type(); - } - - /* - this->properties->registerProperty("connection_packets_sent_speech", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_sent_speech", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_packets_received_speech", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_received_speech", 0, PROP_STATISTIC); - - this->properties->registerProperty("connection_packets_sent_keepalive", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_sent_keepalive", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_packets_received_keepalive", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_received_keepalive", 0, PROP_STATISTIC); - - this->properties->registerProperty("connection_packets_sent_control", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_sent_control", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_packets_received_control", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_received_control", 0, PROP_STATISTIC); - - this->properties->registerProperty("connection_packets_sent_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_sent_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_packets_received_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bytes_received_total", 0, PROP_STATISTIC); - - this->properties->registerProperty("connection_bandwidth_sent_last_second_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bandwidth_sent_last_minute_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bandwidth_received_last_second_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_bandwidth_received_last_minute_total", 0, PROP_STATISTIC); - - this->properties->registerProperty("connection_filetransfer_bandwidth_sent", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bandwidth_received", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bytes_sent_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bytes_received_total", 0, PROP_STATISTIC); - */ } ConnectionStatistics::~ConnectionStatistics() { memtrack::freed(this); - - { - lock_guard lock(this->history_lock_incoming); - - for(auto entry : this->history_incoming) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - for(auto entry : this->history_file_incoming) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - - this->history_incoming.clear(); - this->history_file_incoming.clear(); - } - { - lock_guard lock(this->history_lock_outgoing); - - for(auto entry : this->history_outgoing) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - for(auto entry : this->history_file_outgoing) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - - this->history_outgoing.clear(); - this->history_file_outgoing.clear(); - } -} - -std::shared_ptr ConnectionStatistics::statistics() { - return this->properties; } void ConnectionStatistics::logIncomingPacket(const category::value &category, size_t size) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = uint16_t(size); + assert(category >= 0 && category <= 2); + this->statistics_second_current.connection_bytes_received[category] += size; + this->statistics_second_current.connection_packets_received[category] += 1; - this->_log_incoming_packet(info_entry, category); -} - -void ConnectionStatistics::_log_incoming_packet(ts::stats::StatisticEntry *info_entry, int8_t index) { - if(index >= 0 && index <= 3) { - this->connection_packets_received[index] ++; - this->connection_bytes_received[index] += info_entry->size; - } - this->connection_packets_received[0] ++; - this->connection_bytes_received[0] += info_entry->size; - - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; - - lock_guard lock(this->history_lock_incoming); - this->history_incoming.push_back(info_entry); - } if(this->handle) - this->handle->_log_incoming_packet(info_entry, index); + this->handle->logIncomingPacket(category, size); } void ConnectionStatistics::logOutgoingPacket(const category::value &category, size_t size) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = uint16_t(size); + assert(category >= 0 && category <= 2); + this->statistics_second_current.connection_bytes_sent[category] += size; + this->statistics_second_current.connection_packets_sent[category] += 1; - this->_log_outgoing_packet(info_entry, category); -} - - -void ConnectionStatistics::_log_outgoing_packet(ts::stats::StatisticEntry *info_entry, int8_t index) { - if(index >= 0 && index <= 3) { - this->connection_packets_sent[index] ++; - this->connection_bytes_sent[index] += info_entry->size; - } - this->connection_packets_sent[0] ++; - this->connection_bytes_sent[0] += info_entry->size; - - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; - - lock_guard lock(this->history_lock_outgoing); - this->history_outgoing.push_back(info_entry); - } if(this->handle) - this->handle->_log_outgoing_packet(info_entry, index); + this->handle->logOutgoingPacket(category, size); } /* file transfer */ -void ConnectionStatistics::logFileTransferIn(uint64_t bytes) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = bytes; - - this->_log_incoming_file_packet(info_entry); -} - -void ConnectionStatistics::_log_incoming_file_packet(ts::stats::StatisticEntry *info_entry) { - this->file_bytes_received += info_entry->size; - - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; - - lock_guard lock(this->history_lock_incoming); - this->history_file_incoming.push_back(info_entry); - } +void ConnectionStatistics::logFileTransferIn(uint32_t bytes) { + this->statistics_second_current.file_bytes_received += bytes; + this->file_bytes_received += bytes; if(this->handle) - this->handle->_log_incoming_file_packet(info_entry); + this->handle->logFileTransferIn(bytes); } -void ConnectionStatistics::logFileTransferOut(uint64_t bytes) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = bytes; - - this->_log_outgoing_file_packet(info_entry); -} - -void ConnectionStatistics::_log_outgoing_file_packet(ts::stats::StatisticEntry *info_entry) { - this->file_bytes_sent += info_entry->size; - - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; - - lock_guard lock(this->history_lock_outgoing); - this->history_file_outgoing.push_back(info_entry); - } +void ConnectionStatistics::logFileTransferOut(uint32_t bytes) { + this->statistics_second_current.file_bytes_sent += bytes; + this->file_bytes_sent += bytes; if(this->handle) - this->handle->_log_outgoing_file_packet(info_entry); + this->handle->logFileTransferOut(bytes); } void ConnectionStatistics::tick() { - StatisticEntry* entry; - { - auto timeout_min = system_clock::now() - minutes(1); + auto now = std::chrono::system_clock::now(); + auto time_difference = this->last_second_tick.time_since_epoch().count() > 0 ? now - this->last_second_tick : std::chrono::seconds{1}; + if(time_difference >= std::chrono::seconds{1}) { + BandwidthEntry current{}; + current.atomic_exchange(this->statistics_second_current); - lock_guard lock(this->history_lock_incoming); + auto period_ms = std::chrono::floor(time_difference).count(); + auto current_normalized = current.mul(1000.0 / period_ms); - while(!this->history_incoming.empty() && (entry = this->history_incoming[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + this->statistics_second = this->statistics_second.mul(.2) + current_normalized.mul(.8); + this->total_statistics += current; - this->history_incoming.pop_front(); - } + auto current_second = std::chrono::floor(now.time_since_epoch()).count(); + if(statistics_minute_offset == 0) + statistics_minute_offset = current_second; - while(!this->history_file_incoming.empty() && (entry = this->history_file_incoming[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - - this->history_file_incoming.pop_front(); - } - } - { - auto timeout_min = system_clock::now() - minutes(1); - lock_guard lock(this->history_lock_outgoing); - - while(!this->history_outgoing.empty() && (entry = this->history_outgoing[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - - this->history_outgoing.pop_front(); - } - - while(!this->history_file_outgoing.empty() && (entry = this->history_file_outgoing[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - - this->history_file_outgoing.pop_front(); - } - } - - if(this->properties) { - auto& _properties = *this->properties; -#define M(type, index) \ - _properties[property::CONNECTION_BYTES_SENT_ ##type] = (uint64_t) this->connection_bytes_sent[index]; \ - _properties[property::CONNECTION_PACKETS_SENT_ ##type] = (uint64_t) this->connection_packets_sent[index]; \ - _properties[property::CONNECTION_BYTES_RECEIVED_ ##type] = (uint64_t) this->connection_bytes_received[index]; \ - _properties[property::CONNECTION_PACKETS_RECEIVED_ ##type] = (uint64_t) this->connection_packets_received[index]; \ - - M(TOTAL, 0); - M(CONTROL, 1); - M(KEEPALIVE, 2); - M(SPEECH, 3); - - _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; - _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; - - _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; - _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; + /* fill all "lost" with the current bandwidth as well */ + while(statistics_minute_offset <= current_second) + this->statistics_minute[statistics_minute_offset++ % this->statistics_minute.size()] = current_normalized; + this->last_second_tick = now; } } - -DataSummery ConnectionStatistics::dataReport() { - DataSummery report{}; - auto minTimeout = system_clock::now() - seconds(1); - - - { - lock_guard lock(this->history_lock_incoming); - - for(const auto& elm : this->history_incoming){ - if(elm->timestamp >= minTimeout) { - report.recv_second += elm->size; - } - - report.recv_minute += elm->size; - } - - for(const auto& elm : this->history_file_incoming) { - report.file_recv += elm->size; - } - } - - { - lock_guard lock(this->history_lock_outgoing); - - for(const auto& elm : this->history_outgoing){ - if(elm->timestamp >= minTimeout) { - report.send_second += elm->size; - } - - report.send_minute += elm->size; - } - - for(const auto& elm : this->history_file_outgoing) { - report.file_send += elm->size; - } - } - - report.recv_minute /= 60; - report.send_minute /= 60; - return report; +BandwidthEntry ConnectionStatistics::minute_stats() const { + BandwidthEntry result{}; + for(const auto& second : this->statistics_minute) + result += second; + return result.mul(1. / (double) this->statistics_minute.size()); } -FullReport ConnectionStatistics::full_report() { - FullReport report{}; +FileTransferStatistics ConnectionStatistics::file_stats() { + FileTransferStatistics result{}; - for(size_t index = 0 ; index < 4; index++) { - report.connection_bytes_sent[index] = (uint64_t) this->connection_bytes_sent[index]; - report.connection_packets_sent[index] = (uint64_t) this->connection_packets_sent[index]; - report.connection_bytes_received[index] = (uint64_t) this->connection_bytes_received[index]; - report.connection_packets_received[index] = (uint64_t) this->connection_packets_received[index]; - } + result.bytes_received = this->file_bytes_received; + result.bytes_sent = this->file_bytes_sent; - report.file_bytes_sent = this->file_bytes_sent; - report.file_bytes_received = this->file_bytes_received; - - return report; + return result; } std::pair ConnectionStatistics::mark_file_bytes() { std::pair result; { - lock_guard lock(this->history_lock_incoming); if(this->mark_file_bytes_received < this->file_bytes_received) result.second = this->file_bytes_received - this->mark_file_bytes_received; this->mark_file_bytes_received = (uint64_t) this->file_bytes_received; } { - - lock_guard lock(this->history_lock_outgoing); - if(this->mark_file_bytes_sent < this->file_bytes_sent) result.first = this->file_bytes_sent - this->mark_file_bytes_sent; this->mark_file_bytes_sent = (uint64_t) this->file_bytes_sent; diff --git a/server/src/ConnectionStatistics.h b/server/src/ConnectionStatistics.h index 488b768..c627d57 100644 --- a/server/src/ConnectionStatistics.h +++ b/server/src/ConnectionStatistics.h @@ -11,41 +11,97 @@ namespace ts { } namespace stats { - struct StatisticEntry { - std::atomic use_count{0}; - std::chrono::time_point timestamp; - uint16_t size = 0; + template + struct BandwidthEntry { + std::array connection_packets_sent{}; + std::array connection_bytes_sent{}; + std::array connection_packets_received{}; + std::array connection_bytes_received{}; + + value_t file_bytes_sent{0}; + value_t file_bytes_received{0}; + + template + inline BandwidthEntry& operator=(const BandwidthEntry& other) { + for(size_t index{0}; index < this->connection_packets_sent.size(); index++) + this->connection_packets_sent[index] = other.connection_packets_sent[index]; + for(size_t index{0}; index < this->connection_bytes_sent.size(); index++) + this->connection_bytes_sent[index] = other.connection_bytes_sent[index]; + for(size_t index{0}; index < this->connection_packets_received.size(); index++) + this->connection_packets_received[index] = other.connection_packets_received[index]; + for(size_t index{0}; index < this->connection_bytes_received.size(); index++) + this->connection_bytes_received[index] = other.connection_bytes_received[index]; + + this->file_bytes_sent = other.file_bytes_sent; + this->file_bytes_received = other.file_bytes_received; + return *this; + } + + template + inline BandwidthEntry mul(double factor) const { + BandwidthEntry result{}; + result = *this; + for(auto& val : result.connection_packets_sent) val *= factor; + for(auto& val : result.connection_bytes_sent) val *= factor; + for(auto& val : result.connection_packets_received) val *= factor; + for(auto& val : result.connection_bytes_received) val *= factor; + + result.file_bytes_sent *= factor; + result.file_bytes_received *= factor; + return result; + } + + template + inline BandwidthEntry& operator+=(const BandwidthEntry& other) { + for(size_t index{0}; index < this->connection_packets_sent.size(); index++) + this->connection_packets_sent[index] += other.connection_packets_sent[index]; + for(size_t index{0}; index < this->connection_bytes_sent.size(); index++) + this->connection_bytes_sent[index] += other.connection_bytes_sent[index]; + for(size_t index{0}; index < this->connection_packets_received.size(); index++) + this->connection_packets_received[index] += other.connection_packets_received[index]; + for(size_t index{0}; index < this->connection_bytes_received.size(); index++) + this->connection_bytes_received[index] += other.connection_bytes_received[index]; + + this->file_bytes_sent += other.file_bytes_sent; + this->file_bytes_received += other.file_bytes_received; + return *this; + } + + template + inline BandwidthEntry operator+(const BandwidthEntry& other) { + return BandwidthEntry{*this} += other; + } + + template + inline void atomic_exchange(BandwidthEntry>& source) { + for(size_t index{0}; index < this->connection_packets_sent.size(); index++) + this->connection_packets_sent[index] = source.connection_packets_sent[index].exchange(0); + for(size_t index{0}; index < this->connection_bytes_sent.size(); index++) + this->connection_bytes_sent[index] = source.connection_bytes_sent[index].exchange(0); + for(size_t index{0}; index < this->connection_packets_received.size(); index++) + this->connection_packets_received[index] = source.connection_packets_received[index].exchange(0); + for(size_t index{0}; index < this->connection_bytes_received.size(); index++) + this->connection_bytes_received[index] = source.connection_bytes_received[index].exchange(0); + + this->file_bytes_sent = source.file_bytes_sent.exchange(0); + this->file_bytes_received = source.file_bytes_received.exchange(0); + } }; - struct DataSummery { - uint32_t send_minute; - uint32_t send_second; - - uint32_t recv_minute; - uint32_t recv_second; - - uint32_t file_recv; - uint32_t file_send; - }; - - struct FullReport { - uint64_t connection_packets_sent[4]{0, 0, 0, 0}; - uint64_t connection_bytes_sent[4]{0, 0, 0, 0}; - uint64_t connection_packets_received[4]{0, 0, 0, 0}; - uint64_t connection_bytes_received[4]{0, 0, 0, 0}; - - uint64_t file_bytes_sent = 0; - uint64_t file_bytes_received = 0; + struct FileTransferStatistics { + uint64_t bytes_received{0}; + uint64_t bytes_sent{0}; }; class ConnectionStatistics { public: struct category { + /* Only three categories. Map unknown to category 0 */ enum value { COMMAND, - ACK, + KEEP_ALIVE, VOICE, - UNKNOWN + UNKNOWN = COMMAND }; constexpr static std::array lookup_table{ @@ -53,10 +109,10 @@ namespace ts { VOICE, /* VoiceWhisper */ COMMAND, /* Command */ COMMAND, /* CommandLow */ - ACK, /* Ping */ - ACK, /* Pong */ - ACK, /* Ack */ - ACK, /* AckLow */ + KEEP_ALIVE, /* Ping */ + KEEP_ALIVE, /* Pong */ + COMMAND, /* Ack */ + COMMAND, /* AckLow */ COMMAND, /* */ UNKNOWN, @@ -76,58 +132,38 @@ namespace ts { return from_type(type.type()); } }; - explicit ConnectionStatistics(const std::shared_ptr& /* root */, bool /* spawn properties */); + explicit ConnectionStatistics(std::shared_ptr /* root */); ~ConnectionStatistics(); - std::shared_ptr statistics(); - - inline void logIncomingPacket(const protocol::ClientPacket& packet) { this->logIncomingPacket(category::from_type(packet.type()), packet.length()); } void logIncomingPacket(const category::value& /* category */, size_t /* length */); - inline void logOutgoingPacket(const protocol::ServerPacket& packet) { this->logOutgoingPacket(category::from_type(packet.type()), packet.length()); } void logOutgoingPacket(const category::value& /* category */, size_t /* length */); - void logFileTransferIn(uint64_t); - void logFileTransferOut(uint64_t); + void logFileTransferIn(uint32_t); + void logFileTransferOut(uint32_t); void tick(); - DataSummery dataReport(); - FullReport full_report(); + [[nodiscard]] inline const BandwidthEntry& total_stats() const { return this->total_statistics; } + [[nodiscard]] inline BandwidthEntry second_stats() const { return this->statistics_second; } + [[nodiscard]] BandwidthEntry minute_stats() const; + FileTransferStatistics file_stats(); std::pair mark_file_bytes(); - - inline bool measure_bandwidths() { return this->_measure_bandwidths; } - void measure_bandwidths(bool flag) { this->_measure_bandwidths = flag; } - - inline bool has_properties() { return !!this->properties; } private: - bool _measure_bandwidths = true; std::shared_ptr handle; - std::shared_ptr properties; + BandwidthEntry total_statistics{}; - std::atomic connection_packets_sent[4]{0, 0, 0, 0}; - std::atomic connection_bytes_sent[4]{0, 0, 0, 0}; - std::atomic connection_packets_received[4]{0, 0, 0, 0}; - std::atomic connection_bytes_received[4]{0, 0, 0, 0}; + BandwidthEntry> statistics_second_current{}; + BandwidthEntry statistics_second{}; /* will be updated every second by the stats from the "current_second" */ + std::array, 60> statistics_minute{}; + uint32_t statistics_minute_offset{0}; /* pointing to the upcoming minute */ + std::chrono::system_clock::time_point last_second_tick{}; - std::atomic file_bytes_sent = 0; - std::atomic file_bytes_received = 0; + std::atomic file_bytes_sent{0}; + std::atomic file_bytes_received{0}; - std::atomic mark_file_bytes_sent = 0; - std::atomic mark_file_bytes_received = 0; - - spin_lock history_lock_outgoing; - spin_lock history_lock_incoming; - std::deque history_file_incoming{}; - std::deque history_file_outgoing{}; - std::deque history_incoming{}; - std::deque history_outgoing{}; - - void _log_incoming_packet(StatisticEntry */* statistics */, int8_t /* type index */); - void _log_outgoing_packet(StatisticEntry* /* statistics */, int8_t /* type index */); - - void _log_incoming_file_packet(StatisticEntry */* statistics */); - void _log_outgoing_file_packet(StatisticEntry* /* statistics */); + uint64_t mark_file_bytes_sent{0}; + uint64_t mark_file_bytes_received{0}; }; } } \ No newline at end of file diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index 4ba07a8..931aac1 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -622,8 +622,8 @@ inline sql::result load_properties(ServerId sid, dequename == "undefined") { + const auto &info = property::find(type, key); + if(info.name == "undefined") { logError(sid, "Found unknown property in database! ({})", key); return 0; } @@ -635,9 +635,9 @@ inline sql::result load_properties(ServerId sid, deque(); + auto data = std::make_unique(); + data->type = &info; data->value = value; - data->type = info; properties.push_back(move(data)); return 0; }); @@ -710,7 +710,7 @@ std::shared_ptr DatabaseHelper::loadServerProperties(const std::shar sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; } - logTrace(serverId, "Updating server property: " + prop.type().name + ". New value: " + prop.value() + ". Query: " + sql); + logTrace(serverId, "Updating server property: " + std::string{prop.type().name} + ". New value: " + prop.value() + ". Query: " + sql); sql::command(this->sql, sql, variable{":sid", serverId}, variable{":type", property::PropertyType::PROP_TYPE_SERVER}, @@ -930,7 +930,7 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar if(!prop.isModified()) return; if((prop.type().flags & property::FLAG_SAVE) == 0 && (type != ClientType::CLIENT_MUSIC || (prop.type().flags & property::FLAG_SAVE_MUSIC) == 0)) { - logTrace(server ? server->getServerId() : 0, "[Property] Not saving property '" + prop.type().name + "', changed for " + to_string(cldbid) + " (New value: " + prop.value() + ")"); + logTrace(server ? server->getServerId() : 0, "[Property] Not saving property '" + std::string{prop.type().name} + "', changed for " + to_string(cldbid) + " (New value: " + prop.value() + ")"); return; } if(!prop.get_handle()) return; @@ -945,7 +945,7 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar prop.setDbReference(true); sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:serverId, :type, :id, :key, :value)"; } - logTrace(server ? server->getServerId() : 0, "[Property] Changed property in db key: " + prop.type().name + " value: " + prop.value()); + logTrace(server ? server->getServerId() : 0, "[Property] Changed property in db key: " + std::string{prop.type().name} + " value: " + prop.value()); sql::command(this->sql, sql, variable{":serverId", server ? server->getServerId() : 0}, variable{":type", prop.type().type_property}, @@ -970,7 +970,7 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar else if(prop.type() == property::CLIENT_LASTCONNECTED) query = "UPDATE `clients` SET `lastConnect` = :value WHERE `serverId` = :serverId AND `cldbid` = :cldbid"; if(query.empty()) return; - debugMessage(server ? server->getServerId() : 0, "[Property] Changing client property '" + prop.type().name + "' for " + to_string(cldbid) + " (New value: " + prop.value() + ", SQL: " + query + ")"); + debugMessage(server ? server->getServerId() : 0, "[Property] Changing client property '" + std::string{prop.type().name} + "' for " + to_string(cldbid) + " (New value: " + prop.value() + ", SQL: " + query + ")"); sql::command(this->sql, query, variable{":serverId", server ? server->getServerId() : 0}, variable{":cldbid", cldbid}, variable{":value", prop.value()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); }); @@ -1124,8 +1124,8 @@ void DatabaseHelper::loadStartupPropertyCache() { } } - auto info = property::impl::info_key(type, key); - if(info == property::PropertyDescription::unknown) { + const auto& info = property::find(type, key); + if(info.is_undefined()) { logError(serverId, "Invalid property ({} | {})", key, type); return 0; } @@ -1150,7 +1150,7 @@ void DatabaseHelper::loadStartupPropertyCache() { } auto entry = make_unique(); - entry->info = info; + entry->info = &info; entry->value = value; entry->id = id; entry->type = type; diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index 7e3c4aa..cce5a6c 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -57,8 +57,8 @@ namespace ts { struct StartupPropertyEntry { property::PropertyType type = property::PropertyType::PROP_TYPE_UNKNOWN; - uint64_t id = 0; - std::shared_ptr info = property::PropertyDescription::unknown; + uint64_t id{0}; + const property::PropertyDescription* info{&property::undefined_property_description}; std::string value; }; @@ -70,7 +70,7 @@ namespace ts { }; struct FastPropertyEntry { - std::shared_ptr type; + const property::PropertyDescription* type; std::string value; }; diff --git a/server/src/InstanceHandler.cpp b/server/src/InstanceHandler.cpp index a5e862d..f9e30be 100644 --- a/server/src/InstanceHandler.cpp +++ b/server/src/InstanceHandler.cpp @@ -41,8 +41,7 @@ extern bool mainThreadActive; InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { serverInstance = this; this->tick_manager = make_shared(config::threads::ticking, "tick task "); - this->statistics = make_shared(nullptr, true); - this->statistics->measure_bandwidths(true); + this->statistics = make_shared(nullptr); std::string error_message{}; this->license_service_ = std::make_shared(); @@ -70,8 +69,8 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { } } - const auto &info = property::impl::info(key); - if(*info == property::SERVERINSTANCE_UNDEFINED) { + const auto &info = property::find(key); + if(info == property::SERVERINSTANCE_UNDEFINED) { logError(0, "Got an unknown instance property " + key); return 0; } @@ -414,7 +413,7 @@ FwIDAQAB startTimestamp = system_clock::now(); this->voiceServerManager->executeAutostart(); - this->scheduler()->schedule(INSTANCE_TICK_NAME, bind(&InstanceHandler::tickInstance, this), milliseconds(100)); + this->scheduler()->schedule(INSTANCE_TICK_NAME, bind(&InstanceHandler::tickInstance, this), milliseconds{500}); return true; } @@ -482,12 +481,12 @@ void InstanceHandler::tickInstance() { ALARM_TIMER(t, "InstanceHandler::tickInstance -> flush", milliseconds(5)); //logger::flush(); } - if(statisticsUpdateTimestamp + seconds(5) < now) { + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick", milliseconds(5)); + this->statistics->tick(); + } + if(statisticsUpdateTimestamp + seconds(1) < now) { statisticsUpdateTimestamp = now; - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick", milliseconds(5)); - this->statistics->tick(); - } { ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick [monthly]", milliseconds(2)); diff --git a/server/src/InstanceHandlerSetup.cpp b/server/src/InstanceHandlerSetup.cpp index 08313b3..5792473 100644 --- a/server/src/InstanceHandlerSetup.cpp +++ b/server/src/InstanceHandlerSetup.cpp @@ -134,8 +134,8 @@ bool InstanceHandler::setupDefaultGroups() { } for(const auto& property : info->properties) { - const auto& prop = property::impl::info(property); - if(*prop == property::SERVERINSTANCE_UNDEFINED) { + const auto& prop = property::find(property); + if(prop.is_undefined()) { logCritical(LOG_INSTANCE, "Invalid template property name: " + property); } else { this->properties()[prop] = group->groupId(); diff --git a/server/src/ServerManagerSnapshotDeploy.cpp b/server/src/ServerManagerSnapshotDeploy.cpp index f1d7b74..cbe5b07 100644 --- a/server/src/ServerManagerSnapshotDeploy.cpp +++ b/server/src/ServerManagerSnapshotDeploy.cpp @@ -201,7 +201,7 @@ std::shared_ptr VirtualServerManager::createServerFromSnapshot(sh auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0; debugMessage(0, "Got server snapshot with version {}", snapshot_version); while(true){ - for(auto &key : arguments[index].keys()){ + for(const auto &key : arguments[index].keys()){ if(key == "end_virtualserver") continue; if(key == "begin_virtualserver") continue; if(snapshot_version == 0) { @@ -559,8 +559,8 @@ std::shared_ptr VirtualServerManager::createServerFromSnapshot(sh if(key == "bot_owner_id") continue; if(key == "bot_id") continue; - const auto& property = property::info(key); - if(property->property_index == property::CLIENT_UNDEFINED) { + const auto& property = property::find(key); + if(property.is_undefined()) { debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string()); continue; } @@ -597,8 +597,8 @@ std::shared_ptr VirtualServerManager::createServerFromSnapshot(sh if(key == "begin_playlist") continue; if(key == "playlist_id") continue; - const auto& property = property::info(key); - if(property->property_index == property::CLIENT_UNDEFINED) { + const auto& property = property::find(key); + if(property.is_undefined()) { debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string()); continue; } @@ -787,12 +787,12 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr(); + cmd[index][std::string{serverProperty.type().name}] = (uint64_t) serverProperty.as_save(); default: break; } } - cmd[index][serverProperty.type().name] = serverProperty.value(); + cmd[index][std::string{serverProperty.type().name}] = serverProperty.value(); } cmd[index++]["end_virtualserver"] = ""; } @@ -807,7 +807,7 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr(); else - cmd[index][channelProperty.type().name] = channelProperty.as(); + cmd[index][std::string{channelProperty.type().name}] = channelProperty.as(); } index++; } @@ -899,7 +899,7 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptrtype->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; if(property->value == property->type->default_value) continue; - cmd[index][property->type->name] = property->value; + cmd[index][std::string{property->type->name}] = property->value; } index++; @@ -931,7 +931,7 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptrtype->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; if(property->value == property->type->default_value) continue; - cmd[index][property->type->name] = property->value; + cmd[index][std::string{property->type->name}] = property->value; } index++; diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index a27c801..56c3c04 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -87,8 +87,7 @@ void VirtualServer::executeServerTick() { if(clientOnline + queryOnline == 0) //We don't need to tick, when server is empty! return; properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = this->channelTree->channel_count(); - properties()[property::VIRTUALSERVER_TOTAL_PING] = this->averagePing(); - + properties()[property::VIRTUALSERVER_TOTAL_PING] = this->generate_network_report().average_ping; END_TIMINGS(timing_update_states); } diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index 0203d15..bb7de36 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -180,7 +180,7 @@ bool VirtualServer::initialize(bool test_properties) { letters = new letter::LetterManager(this); - serverStatistics = make_shared(serverInstance->getStatistics(), true); + serverStatistics = make_shared(serverInstance->getStatistics()); this->serverRoot = std::make_shared(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as(), false); static_pointer_cast(this->serverRoot)->setSharedLock(this->serverRoot); @@ -216,12 +216,13 @@ bool VirtualServer::initialize(bool test_properties) { property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, }) { - auto info = property::impl::info(type); + const auto& info = property::describe(type); auto prop = this->properties()[type]; if(prop.default_value() == prop.value()) continue; - if(!info->validate_input(this->properties()[type].value())) { - this->properties()[type] = info->default_value; - logMessage(this->getServerId(), "Server property " + info->name + " contains an invalid value! Resetting it."); + + if(!info.validate_input(this->properties()[type].value())) { + this->properties()[type] = info.default_value; + logMessage(this->getServerId(), "Server property " + std::string{info.name} + " contains an invalid value! Resetting it."); } } @@ -711,8 +712,8 @@ bool VirtualServer::notifyServerEdited(std::shared_ptr invoker, cmd["invokeruid"] = invoker->getUid(); cmd["reasonid"] = ViewReasonId::VREASON_EDITED; for(const auto& key : keys) { - auto info = property::impl::info(key); - if(*info == property::VIRTUALSERVER_UNDEFINED) { + const auto& info = property::find(key); + if(info == property::VIRTUALSERVER_UNDEFINED) { logError(this->getServerId(), "Tried to broadcast a server update with an unknown info: " + key); continue; } @@ -724,7 +725,7 @@ bool VirtualServer::notifyServerEdited(std::shared_ptr invoker, return true; } -bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr client, const deque>& keys, bool selfNotify) { +bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr client, const deque& keys, bool selfNotify) { if(keys.empty()) return false; this->forEachClient([&](const shared_ptr& cl) { shared_lock client_channel_lock(client->channel_lock); @@ -1017,31 +1018,29 @@ bool VirtualServer::verifyServerPassword(std::string password, bool hashed) { return password == this->properties()[property::VIRTUALSERVER_PASSWORD].as(); } -float VirtualServer::averagePacketLoss() { - //TODO Average packet loss - return 0.f; -} +VirtualServer::NetworkReport VirtualServer::generate_network_report() { + double total_ping{0}, total_loss{0}; + size_t pings_counted{0}, loss_counted{0}; -float VirtualServer::averagePing() { - float count = 0; - float sum = 0; - - this->forEachClient([&count, &sum](shared_ptr client) { - auto type = client->getType(); - if(type == ClientType::CLIENT_TEAMSPEAK || type == ClientType::CLIENT_TEASPEAK) { - count++; - sum += duration_cast(dynamic_pointer_cast(client)->calculatePing()).count(); + this->forEachClient([&](const std::shared_ptr& client) { + if(auto vc = dynamic_pointer_cast(client); vc) { + total_ping += vc->current_ping().count(); + total_loss += vc->current_packet_loss(); + pings_counted++; + loss_counted++; } #ifdef COMPILE_WEB_CLIENT - else if(type == ClientType::CLIENT_WEB) { - count++; - sum += duration_cast(dynamic_pointer_cast(client)->client_ping()).count(); + else if(client->getType() == ClientType::CLIENT_WEB) { + pings_counted++; + total_ping += duration_cast(dynamic_pointer_cast(client)->client_ping()).count(); } #endif }); - if(count == 0) return 0; - return sum / count; + VirtualServer::NetworkReport result{}; + if(loss_counted) result.average_loss = total_loss / loss_counted; + if(pings_counted) result.average_ping = total_ping / pings_counted; + return result; } bool VirtualServer::resetPermissions(std::string& token) { diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index 6ff1131..a40b68d 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -136,6 +136,11 @@ namespace ts { friend class InstanceHandler; friend class VirtualServerManager; public: + struct NetworkReport { + float average_ping{0}; + float average_loss{0}; + }; + VirtualServer(ServerId serverId, sql::SqlManager*); ~VirtualServer(); @@ -182,11 +187,11 @@ namespace ts { inline GroupManager* getGroupManager() { return this->groups; } bool notifyServerEdited(std::shared_ptr, std::deque keys); - bool notifyClientPropertyUpdates(std::shared_ptr, const std::deque>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */ + bool notifyClientPropertyUpdates(std::shared_ptr, const std::deque& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */ inline bool notifyClientPropertyUpdates(const std::shared_ptr& client, const std::deque& keys, bool selfNotify = true) { if(keys.empty()) return false; - std::deque> _keys; - for(const auto& key : keys) _keys.push_back(property::impl::info(key)); + std::deque _keys{}; + for(const auto& key : keys) _keys.push_back(&property::describe(key)); return this->notifyClientPropertyUpdates(client, _keys, selfNotify); }; @@ -230,8 +235,7 @@ namespace ts { void testBanStateChange(const std::shared_ptr& invoker); - float averagePing(); - float averagePacketLoss(); + [[nodiscard]] NetworkReport generate_network_report(); bool resetPermissions(std::string&); void ensureValidDefaultGroups(); diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 70af7fd..db7bd4b 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -29,9 +29,7 @@ ConnectedClient::ConnectedClient(sql::SqlManager* db, const std::shared_ptr(this); memset(&this->remote_address, 0, sizeof(this->remote_address)); - connectionStatistics = make_shared(server ? server->getServerStatistics() : nullptr, false); - this->connectionStatistics->measure_bandwidths(false); /* done by the client and we trust this */ - + connectionStatistics = make_shared(server ? server->getServerStatistics() : nullptr); channels = make_shared(this); } @@ -744,17 +742,14 @@ void ConnectedClient::tick(const std::chrono::system_clock::time_point &time) { } - if(this->last_statistics_tick + seconds(5) < time) { - this->last_statistics_tick = time; - this->connectionStatistics->tick(); - } + this->connectionStatistics->tick(); } void ConnectedClient::sendServerInit() { Command command("initserver"); for(const auto& prop : this->server->properties().list_properties(property::FLAG_SERVER_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - command[prop.type().name] = prop.value(); + command[std::string{prop.type().name}] = prop.value(); } command["virtualserver_maxclients"] = 32; diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index e91f310..8602d30 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -135,7 +135,7 @@ namespace ts { virtual bool notifyClientPoke(std::shared_ptr invoker, std::string msg); virtual bool notifyClientUpdated( const std::shared_ptr &, - const std::deque> &, + const std::deque &, bool lock_channel_tree ); /* invalid client id causes error: invalid clientID */ @@ -326,7 +326,6 @@ namespace ts { std::chrono::system_clock::time_point lastOnlineTimestamp; std::chrono::system_clock::time_point lastTransfareTimestamp; std::chrono::system_clock::time_point idleTimestamp; - std::chrono::system_clock::time_point last_statistics_tick; struct { std::mutex lock; diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 3c930c4..20852a9 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -60,7 +60,7 @@ bool ConnectedClient::notifyServerGroupList() { cmd[index]["sgid"] = group->groupId(); } for (const auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - cmd[index][prop.type().name] = prop.value(); + cmd[index][std::string{prop.type().name}] = prop.value(); auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); @@ -186,7 +186,7 @@ bool ConnectedClient::notifyChannelGroupList() { cmd[index]["sgid"] = group->groupId(); } for (auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - cmd[index][prop.type().name] = prop.value(); + cmd[index][std::string{prop.type().name}] = prop.value(); auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); @@ -271,108 +271,116 @@ bool ConnectedClient::notifyClientChannelGroupChanged( } bool ConnectedClient::notifyConnectionInfo(const shared_ptr &target, const shared_ptr &info) { - Command notify("notifyconnectioninfo"); - notify["clid"] = target->getClientId(); + command_builder notify{"notifyconnectioninfo"}; + auto bulk = notify.bulk(0); + bulk.put_unchecked("clid", target->getClientId()); auto not_set = this->getType() == CLIENT_TEAMSPEAK ? 0 : -1; /* we deliver data to the web client as well, because its a bit dump :D */ if(target->getClientId() != this->getClientId()) { - auto report = target->connectionStatistics->full_report(); + auto file_stats = target->connectionStatistics->file_stats(); /* default values which normally sets the client */ - notify["connection_bandwidth_received_last_minute_control"] = not_set; - notify["connection_bandwidth_received_last_minute_keepalive"] = not_set; - notify["connection_bandwidth_received_last_minute_speech"] = not_set; - notify["connection_bandwidth_received_last_second_control"] = not_set; - notify["connection_bandwidth_received_last_second_keepalive"] = not_set; - notify["connection_bandwidth_received_last_second_speech"] = not_set; + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_SPEECH, not_set); - notify["connection_bandwidth_sent_last_minute_control"] = not_set; - notify["connection_bandwidth_sent_last_minute_keepalive"] = not_set; - notify["connection_bandwidth_sent_last_minute_speech"] = not_set; - notify["connection_bandwidth_sent_last_second_control"] = not_set; - notify["connection_bandwidth_sent_last_second_keepalive"] = not_set; - notify["connection_bandwidth_sent_last_second_speech"] = not_set; + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_SECOND_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_SECOND_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_SECOND_SPEECH, not_set); /* its flipped here because the report is out of the clients view */ - notify["connection_bytes_received_control"] = not_set; - notify["connection_bytes_received_keepalive"] = not_set; - notify["connection_bytes_received_speech"] = not_set; - notify["connection_bytes_sent_control"] = not_set; - notify["connection_bytes_sent_keepalive"] = not_set; - notify["connection_bytes_sent_speech"] = not_set; + bulk.put_unchecked(property::CONNECTION_BYTES_RECEIVED_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BYTES_RECEIVED_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BYTES_RECEIVED_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_BYTES_SENT_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_BYTES_SENT_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_BYTES_SENT_SPEECH, not_set); /* its flipped here because the report is out of the clients view */ - notify["connection_packets_received_control"] = not_set; - notify["connection_packets_received_keepalive"] = not_set; - notify["connection_packets_received_speech"] = not_set; - notify["connection_packets_sent_control"] = not_set; - notify["connection_packets_sent_keepalive"] = not_set; - notify["connection_packets_sent_speech"] = not_set; + bulk.put_unchecked(property::CONNECTION_PACKETS_RECEIVED_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_PACKETS_RECEIVED_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_PACKETS_SENT_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_PACKETS_SENT_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_PACKETS_SENT_SPEECH, not_set); - notify["connection_server2client_packetloss_control"] = not_set; - notify["connection_server2client_packetloss_keepalive"] = not_set; - notify["connection_server2client_packetloss_speech"] = not_set; - notify["connection_server2client_packetloss_total"] = not_set; + bulk.put_unchecked(property::CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, not_set); - notify["connection_ping"] = 0; - notify["connection_ping_deviation"] = 0; + bulk.put_unchecked(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_SPEECH, not_set); + bulk.put_unchecked(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_KEEPALIVE, not_set); + bulk.put_unchecked(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_CONTROL, not_set); + bulk.put_unchecked(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_TOTAL, not_set); - notify["connection_connected_time"] = 0; - notify["connection_idle_time"] = 0; + bulk.put_unchecked(property::CONNECTION_PING, 0); + bulk.put_unchecked(property::CONNECTION_PING_DEVIATION, 0); + + bulk.put_unchecked(property::CONNECTION_CONNECTED_TIME, 0); + bulk.put_unchecked(property::CONNECTION_IDLE_TIME, 0); /* its flipped here because the report is out of the clients view */ - notify["connection_filetransfer_bandwidth_sent"] = report.file_bytes_received; - notify["connection_filetransfer_bandwidth_received"] = report.file_bytes_sent; + bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT, file_stats.bytes_received); + bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, file_stats.bytes_sent); } if(info) { - for(const auto& elm : info->properties) { - notify[elm.first] = elm.second; - } + for(const auto& [key, value] : info->properties) + bulk.put(key, value); } else { - //Fill in some server stuff - if(dynamic_pointer_cast(target)) - notify["connection_ping"] = floor(dynamic_pointer_cast(target)->calculatePing()).count(); + //Fill in what we can, else we trust the client + if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) { + auto& stats = target->connectionStatistics->total_stats(); + /* its flipped here because the report is out of the clients view */ + bulk.put(property::CONNECTION_BYTES_RECEIVED_CONTROL, stats.connection_bytes_received[stats::ConnectionStatistics::category::COMMAND]); + bulk.put(property::CONNECTION_BYTES_RECEIVED_KEEPALIVE, stats.connection_bytes_received[stats::ConnectionStatistics::category::KEEP_ALIVE]); + bulk.put(property::CONNECTION_BYTES_RECEIVED_SPEECH, stats.connection_bytes_received[stats::ConnectionStatistics::category::VOICE]); + bulk.put(property::CONNECTION_BYTES_SENT_CONTROL, stats.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]); + bulk.put(property::CONNECTION_BYTES_SENT_KEEPALIVE, stats.connection_bytes_sent[stats::ConnectionStatistics::category::KEEP_ALIVE]); + bulk.put(property::CONNECTION_BYTES_SENT_SPEECH, stats.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]); + + /* its flipped here because the report is out of the clients view */ + bulk.put(property::CONNECTION_PACKETS_RECEIVED_CONTROL, stats.connection_packets_received[stats::ConnectionStatistics::category::COMMAND]); + bulk.put(property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE, stats.connection_packets_received[stats::ConnectionStatistics::category::KEEP_ALIVE]); + bulk.put(property::CONNECTION_PACKETS_RECEIVED_SPEECH, stats.connection_packets_received[stats::ConnectionStatistics::category::VOICE]); + bulk.put(property::CONNECTION_PACKETS_SENT_CONTROL, stats.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]); + bulk.put(property::CONNECTION_PACKETS_SENT_KEEPALIVE, stats.connection_packets_sent[stats::ConnectionStatistics::category::KEEP_ALIVE]); + bulk.put(property::CONNECTION_PACKETS_SENT_SPEECH, stats.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]); + } + } + if(auto vc = dynamic_pointer_cast(target); vc) { + bulk.put(property::CONNECTION_PING, floor(vc->current_ping()).count()); + bulk.put(property::CONNECTION_PING_DEVIATION, vc->current_ping_deviation()); + } #ifdef COMPILE_WEB_CLIENT - else if(dynamic_pointer_cast(target)) - notify["connection_ping"] = floor(dynamic_pointer_cast(target)->client_ping()).count(); + else if(dynamic_pointer_cast(target)) + bulk.put(property::CONNECTION_PING, floor(dynamic_pointer_cast(target)->client_ping()).count()); #endif - if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) { - auto report = target->connectionStatistics->full_report(); - - /* its flipped here because the report is out of the clients view */ - notify["connection_bytes_received_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_bytes_received_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_bytes_received_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; - notify["connection_bytes_sent_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_bytes_sent_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_bytes_sent_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; - - /* its flipped here because the report is out of the clients view */ - notify["connection_packets_received_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_packets_received_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_packets_received_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; - notify["connection_packets_sent_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_packets_sent_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_packets_sent_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; - } + if(auto vc = dynamic_pointer_cast(target); vc){ + auto& calculator = vc->connection->packet_statistics(); + auto report = calculator.loss_report(); + bulk.put(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_SPEECH, std::to_string(report.voice_loss())); + bulk.put(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_KEEPALIVE, std::to_string(report.keep_alive_loss())); + bulk.put(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_CONTROL, std::to_string(report.control_loss())); + bulk.put(property::CONNECTION_CLIENT2SERVER_PACKETLOSS_TOTAL, std::to_string(report.total_loss())); } if(target->getClientId() == this->getClientId() || permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_remoteaddress_view, this->getChannelId()))) { - notify["connection_client_ip"] = target->getLoggingPeerIp(); - notify["connection_client_port"] = target->getPeerPort(); + bulk.put(property::CONNECTION_CLIENT_IP, target->getLoggingPeerIp()); + bulk.put(property::CONNECTION_CLIENT_PORT, target->getPeerPort()); } - //Needs to be filled out - notify["connection_client2server_packetloss_speech"] = not_set; - notify["connection_client2server_packetloss_keepalive"] = not_set; - notify["connection_client2server_packetloss_control"] = not_set; - notify["connection_client2server_packetloss_total"] = not_set; - - notify["connection_connected_time"] = chrono::duration_cast(chrono::system_clock::now() - target->connectTimestamp).count(); - notify["connection_idle_time"] = chrono::duration_cast(chrono::system_clock::now() - target->idleTimestamp).count(); + bulk.put(property::CONNECTION_CONNECTED_TIME, chrono::duration_cast(chrono::system_clock::now() - target->connectTimestamp).count()); + bulk.put(property::CONNECTION_IDLE_TIME, chrono::duration_cast(chrono::system_clock::now() - target->idleTimestamp).count()); this->sendCommand(notify); return true; } @@ -404,7 +412,7 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr &clien return true; } -bool ConnectedClient::notifyClientUpdated(const std::shared_ptr &client, const deque> &props, bool lock) { +bool ConnectedClient::notifyClientUpdated(const std::shared_ptr &client, const deque &props, bool lock) { shared_lock channel_lock(this->channel_lock, defer_lock); if(lock) channel_lock.lock(); @@ -420,7 +428,7 @@ bool ConnectedClient::notifyClientUpdated(const std::shared_ptr Command response("notifyclientupdated"); response["clid"] = client_id; for (const auto &prop : props) { - if(lastOnlineTimestamp.time_since_epoch().count() > 0 && (prop->property_index == property::CLIENT_TOTAL_ONLINE_TIME || prop->property_index == property::CLIENT_MONTH_ONLINE_TIME)) + if(lastOnlineTimestamp.time_since_epoch().count() > 0 && (*prop == property::CLIENT_TOTAL_ONLINE_TIME || *prop == property::CLIENT_MONTH_ONLINE_TIME)) response[prop->name] = client->properties()[prop].as() + duration_cast(system_clock::now() - client->lastOnlineTimestamp).count(); else response[prop->name] = client->properties()[prop].value(); @@ -650,7 +658,7 @@ bool ConnectedClient::notifyClientEnterView(const std::dequevisibleClients.push_back(client); for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - cmd[index][elm.type().name] = elm.value(); + cmd[index][std::string{elm.type().name}] = elm.value(); } index++; @@ -678,14 +686,14 @@ bool ConnectedClient::notifyChannelEdited( Command notify("notifychanneledited"); for(auto prop : properties) { - const auto& prop_info = property::impl::info(prop); + const auto& prop_info = property::describe(prop); if(prop == property::CHANNEL_ORDER) - notify[prop_info->name] = v_channel->previous_channel; + notify[prop_info.name] = v_channel->previous_channel; else if(prop == property::CHANNEL_DESCRIPTION) { send_description_change = true; } else { - notify[prop_info->name] = channel->properties()[prop].as(); + notify[prop_info.name] = channel->properties()[prop].as(); } } diff --git a/server/src/client/ConnectedClientTextCommandHandler.cpp b/server/src/client/ConnectedClientTextCommandHandler.cpp index 690ca48..7cdd0c4 100644 --- a/server/src/client/ConnectedClientTextCommandHandler.cpp +++ b/server/src/client/ConnectedClientTextCommandHandler.cpp @@ -486,7 +486,7 @@ bool ConnectedClient::handle_text_command( for(const auto& property : bot->properties()->list_properties(~0)) { if(find(editable_properties.begin(), editable_properties.end(), property.type().name) == editable_properties.end()) continue; - send_message(bot, " - " + property.type().name + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : "")); + send_message(bot, " - " + std::string{property.type().name} + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : "")); } } else if(arguments.size() < 4) { if(find(editable_properties.begin(), editable_properties.end(), arguments[2]) == editable_properties.end()) { @@ -494,14 +494,14 @@ bool ConnectedClient::handle_text_command( return true; } - const std::shared_ptr &property_info = property::info(arguments[2]); - if(!property_info || property_info->type_property == property::PROP_TYPE_UNKNOWN || property_info->property_index == property::CLIENT_UNDEFINED) { + const auto &property_info = property::find(arguments[2]); + if(property_info.is_undefined()) { send_message(bot, "Unknown property " + arguments[2] + "."); return true; } - auto prop = bot->properties()[(property::ClientProperties) property_info->property_index]; - send_message(bot, "Bot property " + property_info->name + " = " + prop.value() + " " + (property_info->default_value == prop.value() ? "(default)" : "")); + auto prop = bot->properties()[(property::ClientProperties) property_info.property_index]; + send_message(bot, "Bot property " + std::string{property_info.name} + " = " + prop.value() + " " + (property_info.default_value == prop.value() ? "(default)" : "")); return true; } else { Command cmd(""); @@ -527,36 +527,36 @@ bool ConnectedClient::handle_text_command( if(arguments.size() < 3) { send_message(bot, "Playlist properties:"); for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) { - send_message(bot, " - " + property.type().name + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : "")); + send_message(bot, " - " + std::string{property.type().name} + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : "")); } } else if(arguments.size() < 4) { - const std::shared_ptr &property_info = property::info(arguments[2]); - if(!property_info || property_info->type_property == property::PROP_TYPE_UNKNOWN || property_info->property_index == property::PLAYLIST_UNDEFINED) { + const auto &property_info = property::find(arguments[2]); + if(property_info.is_undefined()) { send_message(bot, "Unknown property " + arguments[2] + "."); return true; } - auto prop = playlist->properties()[(property::PlaylistProperties) property_info->property_index]; - send_message(bot, "Bot property " + property_info->name + " = " + prop.value() + " " + (property_info->default_value == prop.value() ? "(default)" : "")); + auto prop = playlist->properties()[(property::PlaylistProperties) property_info.property_index]; + send_message(bot, "Bot property " + std::string{property_info.name} + " = " + prop.value() + " " + (property_info.default_value == prop.value() ? "(default)" : "")); } else { - const std::shared_ptr &property_info = property::info(arguments[2]); - if(!property_info || property_info->type_property == property::PROP_TYPE_UNKNOWN || property_info->property_index == property::PLAYLIST_UNDEFINED) { + const auto &property_info = property::find(arguments[2]); + if(property_info.is_undefined()) { send_message(bot, "Unknown property " + arguments[2] + "."); return true; } JOIN_ARGS(value, 3); - if(!property_info->validate_input(value)) { + if(!property_info.validate_input(value)) { send_message(bot, "Please enter a valid value!"); return true; } - if((property_info->flags & property::FLAG_USER_EDITABLE) == 0) { + if((property_info.flags & property::FLAG_USER_EDITABLE) == 0) { send_message(bot, "This property isnt changeable!"); return true; } - playlist->properties()[(property::PlaylistProperties) property_info->property_index] = value; + playlist->properties()[(property::PlaylistProperties) property_info.property_index] = value; send_message(bot, "Property successfully changed"); return true; } @@ -647,6 +647,16 @@ bool ConnectedClient::handle_text_command( send_message(_this.lock(), " IN " + type.name() + " => generation: " + to_string(genestis[type.type()].generation()) + " id: " + to_string(genestis[type.type()].current_packet_id())); } return true; + } else if(TARG(0, "ping")) { + auto vc = dynamic_pointer_cast(_this.lock()); + if(!vc) return false; + + auto& ack = vc->connection->getAcknowledgeManager(); + send_message(_this.lock(), "Command retransmission values:"); + send_message(_this.lock(), " RTO : " + std::to_string(ack.current_rto())); + send_message(_this.lock(), " RTTVAR: " + std::to_string(ack.current_rttvar())); + send_message(_this.lock(), " SRTT : " + std::to_string(ack.current_srtt())); + return true; } else if(TARG(0, "sgeneration")) { TLEN(4); diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h index 48fc12e..2872187 100644 --- a/server/src/client/DataClient.h +++ b/server/src/client/DataClient.h @@ -45,10 +45,17 @@ namespace ts { return std::forward(f)(*handle); } + /* template ts::PropertyWrapper operator[](T type) { return (*handle)[type]; } + */ + + template + ts::PropertyWrapper operator[](const T& type) { + return (*handle)[type]; + } }; DataClient(sql::SqlManager*, const std::shared_ptr&); diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 9bc0e53..e176dc9 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -479,13 +479,13 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { } } - const auto &info = property::info(key); - if(*info == property::CLIENT_UNDEFINED) { + const auto &info = property::find(key); + if(info.is_undefined()) { logError(this->getServerId(), "{} Tried to pass a unknown value {}. Please report this, if you're sure that this key should be known!", CLIENT_STR_LOG_PREFIX, key); continue; //return {findError("parameter_invalid"), "Unknown property " + key}; } - if(!info->validate_input(cmd[key].as())) + if(!info.validate_input(cmd[key].as())) return command_result{error::parameter_invalid}; this->properties()[info] = cmd[key].as(); diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 8f8431c..7277bc3 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -737,14 +737,14 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) { if (prop == "cpid") continue; if (prop == "cid") continue; - const auto &property = property::info(prop); - if(*property == property::CHANNEL_UNDEFINED) { + const auto &property = property::find(prop); + if(property == property::CHANNEL_UNDEFINED) { logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + prop); continue; } - if(!property->validate_input(cmd[prop].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + property->name + "')"); + if(!property.validate_input(cmd[prop].as())) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + std::string{property.name} + "')"); continue; } created_channel->properties()[property] = cmd[prop].as(); @@ -847,7 +847,7 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { return command_result{error::ok}; } - std::deque> keys; + std::deque keys; bool require_write_lock = false; bool update_max_clients = false; bool update_max_family_clients = false; @@ -866,23 +866,23 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { if(key == "return_code") continue; - const auto &property = property::info(key); - if(*property == property::CHANNEL_UNDEFINED) { + const auto &property = property::find(key); + if(property == property::CHANNEL_UNDEFINED) { logError(this->getServerId(), R"({} Tried to edit a not existing channel property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { + if((property.flags & property::FLAG_USER_EDITABLE) == 0) { logError(this->getServerId(), "{} Tried to change a channel property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if(!property->validate_input(cmd[key].as())) { + if(!property.validate_input(cmd[key].as())) { logError(this->getServerId(), "{} Tried to change a channel property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if(channel->properties()[*property].as() == cmd[key].as()) + if(channel->properties()[property].as() == cmd[key].as()) continue; /* we dont need to update stuff which is the same */ if(key == "channel_icon_id") { @@ -972,7 +972,7 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { ); continue; } - keys.push_back(property); + keys.push_back(&property); } unique_lock server_channel_w_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(), defer_lock); @@ -992,11 +992,11 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { return command_result{error::parameter_missing}; else cmd["channel_password"] = ""; /* no password set */ - keys.push_back(property::info(property::CHANNEL_PASSWORD)); + keys.push_back(&property::describe(property::CHANNEL_PASSWORD)); } if(!cmd[0].has("channel_flag_password")) { cmd["channel_flag_password"] = !cmd["channel_password"].string().empty(); - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_PASSWORD)); } if(cmd["channel_flag_password"].as()) { @@ -1019,20 +1019,20 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) { cmd["channel_flag_password"] = false; cmd["channel_password"] = ""; - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_PASSWORD)); } if(cmd[0].has("channel_flag_default")) { cmd["channel_maxclients"] = -1; cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); update_max_clients = true; cmd["channel_maxfamilyclients"] = -1; cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); update_max_family_clients = true; } } @@ -1043,15 +1043,15 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { if(channel->properties()[property::CHANNEL_MAXCLIENTS].as() != -1) { cmd["channel_maxclients"] = -1; cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); update_max_clients = true; } if(channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as() != -1) { cmd["channel_maxfamilyclients"] = -1; cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); update_max_family_clients = true; } } @@ -1074,12 +1074,12 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { cmd["channel_maxclients"] = -1; else return command_result{error::parameter_missing, "channel_maxclients"}; /* max clients must be specified */ - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS)); } if(!cmd[0].has("channel_flag_maxclients_unlimited")) { cmd["channel_flag_maxclients_unlimited"] = cmd["channel_maxclients"].as() < 0; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); } if(cmd["channel_flag_maxclients_unlimited"].as() && cmd["channel_maxclients"].as() != -1) @@ -1097,17 +1097,17 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { cmd["channel_flag_maxfamilyclients_inherited"] = false; else cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); } else if(cmd[0].has("channel_flag_maxfamilyclients_inherited")) { if(cmd["channel_flag_maxfamilyclients_inherited"].as()) cmd["channel_flag_maxfamilyclients_unlimited"] = false; else cmd["channel_flag_maxfamilyclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED)); + keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED)); } else /* not really possible */ return command_result{error::parameter_missing, "channel_maxfamilyclients"}; /* family max clients must be */ cmd["channel_maxfamilyclients"] = -1; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); + keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS)); } //keep this order because this command: "channeledit cid= channel_maxfamilyclients=-1" should set max family clients mode to inherited if(!cmd[0].has("channel_flag_maxfamilyclients_inherited")) { @@ -1148,10 +1148,10 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { auto self_ref = this->ref(); shared_ptr old_default_channel; deque> child_channel_updated; - for(const std::shared_ptr& key : keys) { + for(const property::PropertyDescription* key : keys) { if(*key == property::CHANNEL_ORDER) { /* TODO: May move that up because if it fails may some other props have already be applied */ - if (!channel_tree->change_order(channel, cmd[key->name])) + if (!channel_tree->change_order(channel, cmd[std::string{key->name}])) return command_result{error::channel_invalid_order, "Can't change order id"}; if(this->server) { @@ -1194,7 +1194,7 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { continue; } - if(!cmd[key->name].as()) { + if(!cmd[std::string{key->name}].as()) { old_default_channel = nullptr; continue; } @@ -1253,13 +1253,13 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { if(conversation_manager) { auto conversation = conversation_manager->get(channel->channelId()); if(conversation) - conversation->set_history_length(cmd[key->name]); + conversation->set_history_length(cmd[std::string{key->name}]); } } else if(*key == property::CHANNEL_NEEDED_TALK_POWER) { channel->permissions()->set_permission(permission::i_client_needed_talk_power, {cmd[key->name].as(), 0}, permission::v2::set_value, permission::v2::do_nothing); } - channel->properties()[key] = cmd[key->name].string(); + channel->properties()[*key] = cmd[std::string{key->name}].string(); } if(this->server) { vector key_vector; diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index a8779ea..ced8596 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -44,9 +44,9 @@ command_result ConnectedClient::handleCommandClientGetVariables(Command &cmd) { if (!client || (client.client != this && !this->isClientVisible(client.client, false))) return command_result{error::client_invalid_id, ""}; - deque> props; + deque props; for (auto &prop : client->properties()->list_properties(property::FLAG_CLIENT_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - props.push_back(property::info((property::ClientProperties) prop.type().property_index)); + props.push_back(&prop.type()); } this->notifyClientUpdated(client.client, props, false); @@ -391,13 +391,13 @@ command_result ConnectedClient::handleCommandClientDBEdit(Command &cmd) { for (auto &elm : cmd[0].keys()) { if (elm == "cldbid") continue; - auto info = property::info(elm); - if(*info == property::CLIENT_UNDEFINED) { + const auto& info = property::find(elm); + if(info == property::CLIENT_UNDEFINED) { logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change someone's db entry, but the entry in unknown: " + elm); continue; } - if(!info->validate_input(cmd[elm].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as() + "', Property: '" + info->name + "')"); + if(!info.validate_input(cmd[elm].as())) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as() + "', Property: '" + std::string{info.name} + "')"); continue; } (*props)[info] = cmd[elm].string(); @@ -423,29 +423,29 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: bool update_talk_rights = false; unique_ptr> nickname_lock; - deque> keys; + std::deque> keys; for(const auto& key : cmd[0].keys()) { if(key == "return_code") continue; if(key == "clid") continue; - const auto &info = property::info(key); - if(*info == property::CLIENT_UNDEFINED) { + const auto &info = property::find(key); + if(info == property::CLIENT_UNDEFINED) { logError(this->getServerId(), R"([{}] Tried to change a not existing client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); continue; } - if((info->flags & property::FLAG_USER_EDITABLE) == 0) { + if((info.flags & property::FLAG_USER_EDITABLE) == 0) { logError(this->getServerId(), R"([{}] Tried to change a not user editable client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); continue; } - if(!info->validate_input(cmd[key].as())) { + if(!info.validate_input(cmd[key].as())) { logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); continue; } - if(client->properties()[info].as() == cmd[key].as()) continue; + if(client->properties()[&info].as() == cmd[key].as()) continue; - if (*info == property::CLIENT_DESCRIPTION) { + if (info == property::CLIENT_DESCRIPTION) { if (self) { ACTION_REQUIRES_PERMISSION(permission::b_client_modify_own_description, 1, client->getChannelId()); } else if(client->getType() == ClientType::CLIENT_MUSIC) { @@ -458,16 +458,16 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: string value = cmd["client_description"].string(); if (count_characters(value) > 200) return command_result{error::parameter_invalid, "Invalid description length. A maximum of 200 characters is allowed!"}; - } else if (*info == property::CLIENT_IS_TALKER) { + } else if (info == property::CLIENT_IS_TALKER) { ACTION_REQUIRES_PERMISSION(permission::b_client_set_flag_talker, 1, client->getChannelId()); cmd["client_is_talker"] = cmd["client_is_talker"].as(); cmd["client_talk_request"] = 0; update_talk_rights = true; - keys.emplace_back(property::CLIENT_IS_TALKER, "client_is_talker"); - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); + keys.emplace_back(&property::describe(property::CLIENT_IS_TALKER), "client_is_talker"); + keys.emplace_back(&property::describe(property::CLIENT_TALK_REQUEST), "client_talk_request"); continue; - } else if(*info == property::CLIENT_NICKNAME) { + } else if(info == property::CLIENT_NICKNAME) { if(!self) { if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { @@ -500,7 +500,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: continue; } } - } else if(*info == property::CLIENT_PLAYER_VOLUME) { + } else if(info == property::CLIENT_PLAYER_VOLUME) { if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { ACTION_REQUIRES_PERMISSION(permission::i_client_music_modify_power, client->calculate_permission(permission::i_client_music_needed_modify_power, client->getChannelId()), client->getChannelId()); @@ -515,7 +515,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: return command_result{permission::i_client_music_create_modify_max_volume}; bot->volume_modifier(cmd["player_volume"]); - } else if(*info == property::CLIENT_IS_CHANNEL_COMMANDER) { + } else if(info == property::CLIENT_IS_CHANNEL_COMMANDER) { if(!self) { if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { @@ -525,7 +525,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: if(cmd["client_is_channel_commander"].as()) ACTION_REQUIRES_PERMISSION(permission::b_client_use_channel_commander, 1, client->getChannelId()); - } else if(*info == property::CLIENT_IS_PRIORITY_SPEAKER) { + } else if(info == property::CLIENT_IS_PRIORITY_SPEAKER) { //FIXME allow other to remove this thing if(!self) { if(client->getType() != ClientType::CLIENT_MUSIC) @@ -544,7 +544,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: cmd["client_talk_request"] = duration_cast(system_clock::now().time_since_epoch()).count(); else cmd["client_talk_request"] = 0; - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); + keys.emplace_back(&property::describe(property::CLIENT_TALK_REQUEST), "client_talk_request"); continue; } else if (self && key == "client_badges") { std::string str = cmd[key]; @@ -576,7 +576,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { ACTION_REQUIRES_PERMISSION(permission::i_client_music_modify_power, client->calculate_permission(permission::i_client_music_needed_modify_power, client->getChannelId()), client->getChannelId()); } - } else if(!self && (*info == property::CLIENT_FLAG_NOTIFY_SONG_CHANGE/* || *info == property::CLIENT_NOTIFY_SONG_MESSAGE*/)) { + } else if(!self && (info == property::CLIENT_FLAG_NOTIFY_SONG_CHANGE/* || info == property::CLIENT_NOTIFY_SONG_MESSAGE*/)) { if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { ACTION_REQUIRES_PERMISSION(permission::i_client_music_modify_power, client->calculate_permission(permission::i_client_music_needed_modify_power, client->getChannelId()), client->getChannelId()); @@ -596,8 +596,8 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: cmd["client_lastconnected"] = value; } - keys.emplace_back(property::CLIENT_LASTCONNECTED, "client_lastconnected"); - } else if(!self && *info == property::CLIENT_BOT_TYPE) { + keys.emplace_back(&property::describe(property::CLIENT_LASTCONNECTED), "client_lastconnected"); + } else if(!self && info == property::CLIENT_BOT_TYPE) { ACTION_REQUIRES_PERMISSION(permission::i_client_music_modify_power, client->calculate_permission(permission::i_client_music_needed_modify_power, client->getChannelId()), client->getChannelId()); auto type = cmd["client_bot_type"].as(); if(type == MusicClient::Type::TEMPORARY) { @@ -608,7 +608,7 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: ACTION_REQUIRES_PERMISSION(permission::b_client_music_modify_permanent, 1, client->getChannelId()); } else return command_result{error::parameter_invalid}; - } else if(*info == property::CLIENT_AWAY_MESSAGE) { + } else if(info == property::CLIENT_AWAY_MESSAGE) { if(!self) continue; if(cmd["client_away_message"].string().length() > 256) @@ -617,12 +617,12 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: continue; } - keys.emplace_back((property::ClientProperties) info->property_index, key); + keys.emplace_back(&info, key); } - deque updates; + deque updates; for(const auto& key : keys) { - if(key.first == property::CLIENT_IS_PRIORITY_SPEAKER) { + if(*key.first == property::CLIENT_IS_PRIORITY_SPEAKER) { client->clientPermissions->set_permission(permission::b_client_is_priority_speaker, {1, 0}, cmd["client_is_priority_speaker"].as() ? permission::v2::PermissionUpdateType::set_value : permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); } client->properties()[key.first] = cmd[0][key.second].value(); @@ -1116,7 +1116,7 @@ command_result ConnectedClient::handleCommandClientInfo(Command &cmd) { } for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - res[result_index][key.type().name] = key.value(); + res[result_index][std::string{key.type().name}] = key.value(); if(view_remote) res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].as(); else diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index a4123e2..38c4717 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -366,7 +366,7 @@ command_result ConnectedClient::handleCommandPermissionList(Command &cmd) { #define M(ptype) \ do { \ - for(const auto& prop : property::impl::list()) { \ + for(const auto& prop : property::list()) { \ if((prop->flags & property::FLAG_INTERNAL) > 0) continue; \ response[index]["name"] = prop->name; \ response[index]["flags"] = prop->flags; \ diff --git a/server/src/client/command_handler/music.cpp b/server/src/client/command_handler/music.cpp index 58a2c33..67ffeb5 100644 --- a/server/src/client/command_handler/music.cpp +++ b/server/src/client/command_handler/music.cpp @@ -447,41 +447,41 @@ command_result ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) { if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_modify_power, permission::i_playlist_modify_power); perr) return command_result{perr}; - deque, string>> properties; + deque> properties; for(const auto& key : cmd[0].keys()) { if(key == "playlist_id") continue; if(key == "return_code") continue; - auto property = property::info(key); - if(*property == property::PLAYLIST_UNDEFINED) { + const auto& property = property::find(key); + if(property == property::PLAYLIST_UNDEFINED) { logError(this->getServerId(), R"([{}] Tried to edit a not existing playlist property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { + if((property.flags & property::FLAG_USER_EDITABLE) == 0) { logError(this->getServerId(), "[{}] Tried to change a playlist property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if(!property->validate_input(cmd[key].as())) { + if(!property.validate_input(cmd[key].as())) { logError(this->getServerId(), "[{}] Tried to change a playlist property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); continue; } - if(*property == property::PLAYLIST_CURRENT_SONG_ID) { + if(property == property::PLAYLIST_CURRENT_SONG_ID) { auto song_id = cmd[key].as(); auto song = song_id > 0 ? playlist->find_song(song_id) : nullptr; if(song_id != 0 && !song) return command_result{error::playlist_invalid_song_id}; - } else if(*property == property::PLAYLIST_MAX_SONGS) { + } else if(property == property::PLAYLIST_MAX_SONGS) { auto value = cmd[key].as(); auto max_value = this->calculate_permission(permission::i_max_playlist_size, this->getChannelId()); if(max_value.has_value && !permission::v2::permission_granted(value, max_value)) return command_result{permission::i_max_playlist_size}; } - properties.emplace_back(property, key); + properties.emplace_back(&property, key); } for(const auto& property : properties) { if(*property.first == property::PLAYLIST_CURRENT_SONG_ID) { diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index 62ae0b5..5a55e9b 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -224,14 +224,14 @@ command_result ConnectedClient::handleCommandServerEdit(Command &cmd) { std::deque keys; bool group_update = false; for (const auto& elm : toApplay) { - auto info = property::impl::info(elm.first); - if(*info == property::VIRTUALSERVER_UNDEFINED) { + const auto& info = property::find(elm.first); + if(info == property::VIRTUALSERVER_UNDEFINED) { logCritical(target_server ? target_server->getServerId() : 0, "Missing server property " + elm.first); continue; } - if(!info->validate_input(elm.second)) { - logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + info->name + "')"); + if(!info.validate_input(elm.second)) { + logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + std::string{info.name} + "')"); continue; } if(target_server) @@ -240,7 +240,7 @@ command_result ConnectedClient::handleCommandServerEdit(Command &cmd) { (*serverInstance->getDefaultServerProperties())[info] = elm.second; keys.push_back(elm.first); - group_update |= *info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || *info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || *info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP; + group_update |= info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP; } if(target_server) { @@ -262,35 +262,39 @@ command_result ConnectedClient::handleCommandServerRequestConnectionInfo(Command CMD_REQ_SERVER; ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_connectioninfo_view, 1); - Command notify("notifyserverconnectioninfo"); + ts::command_builder result{"notifyserverconnectioninfo"}; + auto first_bulk = result.bulk(0); - auto statistics = this->server->getServerStatistics()->statistics(); - auto report = this->server->getServerStatistics()->dataReport(); + auto total_stats = this->server->getServerStatistics()->total_stats(); + auto minute_report = this->server->getServerStatistics()->minute_stats(); + auto second_report = this->server->getServerStatistics()->second_stats(); + auto network_report = this->server->generate_network_report(); - notify[0]["connection_filetransfer_bandwidth_sent"] = report.file_send; - notify[0]["connection_filetransfer_bandwidth_received"] = report.file_recv; + first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT, minute_report.file_bytes_sent); + first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, minute_report.file_bytes_received); - notify[0]["connection_filetransfer_bytes_sent_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); - notify[0]["connection_filetransfer_bytes_received_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); + first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, minute_report.file_bytes_sent); + first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, minute_report.file_bytes_received); - notify[0]["connection_filetransfer_bytes_sent_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); - notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); + first_bulk.put_unchecked("connection_filetransfer_bytes_sent_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as()); + first_bulk.put_unchecked("connection_filetransfer_bytes_received_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as()); - notify[0]["connection_packets_sent_total"] = (*statistics)[property::CONNECTION_PACKETS_SENT_TOTAL].as(); - notify[0]["connection_bytes_sent_total"] = (*statistics)[property::CONNECTION_BYTES_SENT_TOTAL].as(); - notify[0]["connection_packets_received_total"] = (*statistics)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as(); - notify[0]["connection_bytes_received_total"] = (*statistics)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as(); + first_bulk.put_unchecked(property::CONNECTION_PACKETS_SENT_TOTAL, std::accumulate(total_stats.connection_packets_sent.begin(), total_stats.connection_packets_sent.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_BYTES_SENT_TOTAL, std::accumulate(total_stats.connection_bytes_sent.begin(), total_stats.connection_bytes_sent.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_PACKETS_RECEIVED_TOTAL, std::accumulate(total_stats.connection_packets_received.begin(), total_stats.connection_packets_received.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_BYTES_RECEIVED_TOTAL, std::accumulate(total_stats.connection_bytes_received.begin(), total_stats.connection_bytes_received.end(), 0U)); - notify[0]["connection_bandwidth_sent_last_second_total"] = report.send_second; - notify[0]["connection_bandwidth_sent_last_minute_total"] = report.send_minute; - notify[0]["connection_bandwidth_received_last_second_total"] = report.recv_second; - notify[0]["connection_bandwidth_received_last_minute_total"] = report.recv_minute; - notify[0]["connection_connected_time"] = this->server->properties()[property::VIRTUALSERVER_UPTIME].as(); - notify[0]["connection_packetloss_total"] = this->server->averagePacketLoss(); - notify[0]["connection_ping"] = this->server->averagePing(); + first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL, std::accumulate(second_report.connection_bytes_sent.begin(), second_report.connection_bytes_sent.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL, std::accumulate(minute_report.connection_bytes_sent.begin(), minute_report.connection_bytes_sent.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL, std::accumulate(second_report.connection_bytes_received.begin(), second_report.connection_bytes_received.end(), 0U)); + first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL, std::accumulate(minute_report.connection_bytes_received.begin(), minute_report.connection_bytes_received.end(), 0U)); - this->sendCommand(notify); + first_bulk.put_unchecked(property::CONNECTION_CONNECTED_TIME, this->server->properties()[property::VIRTUALSERVER_UPTIME].as()); + first_bulk.put_unchecked(property::CONNECTION_PACKETLOSS_TOTAL, network_report.average_loss); + first_bulk.put_unchecked(property::CONNECTION_PING, network_report.average_ping); + + this->sendCommand(result); return command_result{error::ok}; } diff --git a/server/src/client/query/QueryClient.h b/server/src/client/query/QueryClient.h index 6d84a55..5d5baf4 100644 --- a/server/src/client/query/QueryClient.h +++ b/server/src/client/query/QueryClient.h @@ -99,7 +99,7 @@ namespace ts::server { bool notifyServerUpdated(std::shared_ptr ptr) override; bool notifyClientPoke(std::shared_ptr invoker, std::string msg) override; - bool notifyClientUpdated(const std::shared_ptr &ptr, const std::deque> &deque, bool lock_channel_tree) override; + bool notifyClientUpdated(const std::shared_ptr &ptr, const std::deque &deque, bool lock_channel_tree) override; bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr) override; bool notifyClientChatComposing(const std::shared_ptr &ptr) override; diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index c1e4589..482a165 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "src/client/command_handler/helpers.h" @@ -397,37 +398,36 @@ command_result QueryClient::handleCommandServerInfo(Command &) { if(this->server && permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_connectioninfo_view, 0))) { - auto stats = this->server->getServerStatistics()->statistics(); - auto report = this->server->serverStatistics->dataReport(); - cmd["connection_bandwidth_sent_last_second_total"] = report.send_second; - cmd["connection_bandwidth_sent_last_minute_total"] = report.send_minute; - cmd["connection_bandwidth_received_last_second_total"] = report.recv_second; - cmd["connection_bandwidth_received_last_minute_total"] = report.recv_minute; + auto total_stats = this->server->getServerStatistics()->total_stats(); + auto report_second = this->server->serverStatistics->second_stats(); + auto report_minute = this->server->serverStatistics->minute_stats(); + cmd["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), 0U); + cmd["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), 0U); + cmd["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), 0U); + cmd["connection_bandwidth_received_last_minute_total"] = std::accumulate(report_minute.connection_bytes_received.begin(), report_minute.connection_bytes_received.end(), 0U); - cmd["connection_filetransfer_bandwidth_sent"] = report.file_send; - cmd["connection_filetransfer_bandwidth_received"] = report.file_recv; - cmd["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); - cmd["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); + cmd["connection_filetransfer_bandwidth_sent"] = report_minute.file_bytes_sent; + cmd["connection_filetransfer_bandwidth_received"] = report_minute.file_bytes_received; - cmd["connection_packets_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT].value(); - cmd["connection_bytes_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED].value(); - cmd["connection_packets_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].value(); - cmd["connection_bytes_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].value(); + cmd["connection_packets_sent_speech"] = total_stats.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; + cmd["connection_bytes_sent_speech"] = total_stats.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; + cmd["connection_packets_received_speech"] = total_stats.connection_packets_received[stats::ConnectionStatistics::category::VOICE]; + cmd["connection_bytes_received_speech"] = total_stats.connection_bytes_received[stats::ConnectionStatistics::category::VOICE]; - cmd["connection_packets_sent_keepalive"] = (*stats)[property::CONNECTION_PACKETS_SENT_KEEPALIVE].value(); - cmd["connection_packets_received_keepalive"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE].value(); - cmd["connection_bytes_received_keepalive"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_KEEPALIVE].value(); - cmd["connection_bytes_sent_keepalive"] = (*stats)[property::CONNECTION_BYTES_SENT_KEEPALIVE].value(); + cmd["connection_packets_sent_keepalive"] = total_stats.connection_packets_sent[stats::ConnectionStatistics::category::KEEP_ALIVE]; + cmd["connection_packets_received_keepalive"] = total_stats.connection_bytes_sent[stats::ConnectionStatistics::category::KEEP_ALIVE]; + cmd["connection_bytes_received_keepalive"] = total_stats.connection_packets_received[stats::ConnectionStatistics::category::KEEP_ALIVE]; + cmd["connection_bytes_sent_keepalive"] = total_stats.connection_bytes_received[stats::ConnectionStatistics::category::KEEP_ALIVE]; - cmd["connection_packets_sent_control"] = (*stats)[property::CONNECTION_PACKETS_SENT_CONTROL].value(); - cmd["connection_bytes_sent_control"] = (*stats)[property::CONNECTION_BYTES_SENT_CONTROL].value(); - cmd["connection_packets_received_control"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_CONTROL].value(); - cmd["connection_bytes_received_control"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_CONTROL].value(); + cmd["connection_packets_sent_control"] = total_stats.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; + cmd["connection_bytes_sent_control"] = total_stats.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; + cmd["connection_packets_received_control"] = total_stats.connection_packets_received[stats::ConnectionStatistics::category::COMMAND]; + cmd["connection_bytes_received_control"] = total_stats.connection_bytes_received[stats::ConnectionStatistics::category::COMMAND]; - cmd["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].value(); - cmd["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].value(); - cmd["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].value(); - cmd["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].value(); + cmd["connection_packets_sent_total"] = std::accumulate(report_second.connection_packets_sent.begin(), report_second.connection_packets_sent.end(), 0U); + cmd["connection_bytes_sent_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), 0U); + cmd["connection_packets_received_total"] = std::accumulate(report_second.connection_packets_received.begin(), report_second.connection_packets_received.end(), 0U); + cmd["connection_bytes_received_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), 0U); } else { cmd["connection_bandwidth_sent_last_second_total"] = "0"; cmd["connection_bandwidth_sent_last_minute_total"] = "0"; @@ -598,12 +598,12 @@ command_result QueryClient::handleCommandServerCreate(Command& cmd) { if(key == "virtualserver_port") continue; if(key == "virtualserver_host") continue; - auto info = property::impl::info(key); - if(*info == property::VIRTUALSERVER_UNDEFINED) { + const auto& info = property::find(key); + if(info == property::VIRTUALSERVER_UNDEFINED) { logError(server->getServerId(), "Tried to change unknown server property " + key); continue; } - if(!info->validate_input(cmd[key].as())) { + if(!info.validate_input(cmd[key].as())) { logError(server->getServerId(), "Tried to change " + key + " to an invalid value: " + cmd[key].as()); continue; } @@ -722,9 +722,9 @@ command_result QueryClient::handleCommandInstanceEdit(Command& cmd) { ACTION_REQUIRES_INSTANCE_PERMISSION(permission::b_serverinstance_modify_settings, 1); for(const auto &key : cmd[0].keys()){ - auto info = property::impl::info(key); + const auto* info = &property::find(key); if(key == "serverinstance_serverquery_max_connections_per_ip") - info = property::impl::info(property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP); + info = &property::describe(property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP); if(*info == property::SERVERINSTANCE_UNDEFINED) { logError(LOG_QUERY, "Query {} tried to change a non existing instance property: {}", this->getLoggingPeerIp(), key); @@ -758,22 +758,24 @@ command_result QueryClient::handleCommandHostInfo(Command &) { res["virtualservers_total_channels_online"] = vsReport.onlineChannels; - auto stats = serverInstance->getStatistics()->statistics(); - res["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].as(); - res["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].as(); - res["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as(); - res["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as(); + auto total_stats = serverInstance->getStatistics()->total_stats(); + res["connection_packets_sent_total"] = std::accumulate(total_stats.connection_packets_sent.begin(), total_stats.connection_packets_sent.end(), 0U); + res["connection_bytes_sent_total"] = std::accumulate(total_stats.connection_bytes_sent.begin(), total_stats.connection_bytes_sent.end(), 0U); + res["connection_packets_received_total"] = std::accumulate(total_stats.connection_packets_received.begin(), total_stats.connection_packets_received.end(), 0U); + res["connection_bytes_received_total"] = std::accumulate(total_stats.connection_bytes_received.begin(), total_stats.connection_bytes_received.end(), 0U); - auto report = serverInstance->getStatistics()->dataReport(); - res["connection_bandwidth_sent_last_second_total"] = report.send_second; - res["connection_bandwidth_sent_last_minute_total"] = report.send_minute; - res["connection_bandwidth_received_last_second_total"] = report.recv_second; - res["connection_bandwidth_received_last_minute_total"] = report.recv_minute; + auto report_second = serverInstance->getStatistics()->second_stats(); + auto report_minute = serverInstance->getStatistics()->minute_stats(); + res["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), 0U); + res["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), 0U); + res["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), 0U); + res["connection_bandwidth_received_last_minute_total"] = std::accumulate(report_minute.connection_bytes_received.begin(), report_minute.connection_bytes_received.end(), 0U); - res["connection_filetransfer_bandwidth_sent"] = report.file_send; - res["connection_filetransfer_bandwidth_received"] = report.file_recv; - res["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); - res["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); + + res["connection_filetransfer_bandwidth_sent"] = report_minute.file_bytes_sent; + res["connection_filetransfer_bandwidth_received"] = report_minute.file_bytes_received; + res["connection_filetransfer_bytes_sent_total"] = total_stats.file_bytes_sent; + res["connection_filetransfer_bytes_received_total"] = total_stats.file_bytes_received; this->sendCommand(res); return command_result{error::ok}; diff --git a/server/src/client/query/QueryClientNotify.cpp b/server/src/client/query/QueryClientNotify.cpp index 6f45233..3e34dfb 100644 --- a/server/src/client/query/QueryClientNotify.cpp +++ b/server/src/client/query/QueryClientNotify.cpp @@ -42,7 +42,7 @@ bool QueryClient::notifyServerUpdated(shared_ptr ptr) { return ConnectedClient::notifyServerUpdated(ptr); } -bool QueryClient::notifyClientUpdated(const std::shared_ptr &ptr, const std::deque> &deque, bool lock_channel_tree) { +bool QueryClient::notifyClientUpdated(const std::shared_ptr &ptr, const std::deque &deque, bool lock_channel_tree) { CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE); return ConnectedClient::notifyClientUpdated(ptr, deque, lock_channel_tree); } diff --git a/server/src/client/voice/PacketStatistics.cpp b/server/src/client/voice/PacketStatistics.cpp new file mode 100644 index 0000000..2a7fad5 --- /dev/null +++ b/server/src/client/voice/PacketStatistics.cpp @@ -0,0 +1,95 @@ +// +// Created by WolverinDEV on 06/04/2020. +// + +#include "PacketStatistics.h" + +using namespace ts::server::client; + +void PacketStatistics::received_packet(ts::protocol::PacketType type, uint32_t pid) { + std::lock_guard lock{this->data_mutex}; + switch (type) { + case protocol::PacketType::VOICE: + this->calculator_voice.packet_received(pid); + return; + case protocol::PacketType::VOICE_WHISPER: + this->calculator_voice_whisper.packet_received(pid); + return; + + case protocol::PacketType::COMMAND: + case protocol::PacketType::COMMAND_LOW: + return; + + case protocol::PacketType::ACK: + this->calculator_ack.packet_received(pid); + return; + case protocol::PacketType::ACK_LOW: + this->calculator_ack_low.packet_received(pid); + return; + case protocol::PacketType::PING: + this->calculator_ping.packet_received(pid); + return; + + default: + /* some invalid packet lul */ + return; + } +} + +void PacketStatistics::send_command(ts::protocol::PacketType type, uint32_t pid) { + std::lock_guard lock{this->data_mutex}; + if(type == protocol::PacketType::COMMAND) + this->calculator_command.packet_send(pid); + else if(type == protocol::PacketType::COMMAND_LOW) + this->calculator_command_low.packet_send(pid); +} + +void PacketStatistics::received_acknowledge(ts::protocol::PacketType type, uint32_t pid) { + std::lock_guard lock{this->data_mutex}; + if(type == protocol::PacketType::ACK) + this->calculator_command.ack_received(pid); + else if(type == protocol::PacketType::ACK_LOW) + this->calculator_command_low.ack_received(pid); +} + +PacketStatistics::PacketLossReport PacketStatistics::loss_report() const { + PacketStatistics::PacketLossReport result{}; + + result.received_voice = this->calculator_voice.received_packets() + this->calculator_voice_whisper.received_packets(); + result.lost_voice = this->calculator_voice.lost_packets() + this->calculator_voice_whisper.lost_packets(); + + result.received_keep_alive = this->calculator_ping.received_packets(); + result.lost_keep_alive = this->calculator_ping.lost_packets(); + + result.received_control = this->calculator_command.received_packets() + this->calculator_command_low.received_packets(); + result.lost_control = this->calculator_command.lost_packets() + this->calculator_command_low.lost_packets(); + //result.lost_control -= this->calculator_ack.lost_packets() + this->calculator_ack_low.lost_packets(); /* subtract the lost acks (command received but ack got lost) */ + + result.received_control += this->calculator_ack.received_packets() + this->calculator_ack_low.received_packets(); + //result.lost_control += this->calculator_ack.lost_packets() + this->calculator_ack_low.lost_packets(); /* this cancels out the line above */ + return result; +} + +void PacketStatistics::tick() { + auto now = std::chrono::system_clock::now(); + if(now + std::chrono::seconds{15} > this->last_short) { + this->last_short = now; + + std::lock_guard lock{this->data_mutex}; + this->calculator_command.short_stats(); + this->calculator_command_low.short_stats(); + + this->calculator_ack.short_stats(); + this->calculator_ack_low.short_stats(); + + this->calculator_voice.short_stats(); + this->calculator_voice_whisper.short_stats(); + + this->calculator_ping.short_stats(); + } +} + +float PacketStatistics::current_packet_loss() const { + auto report = this->loss_report(); + return report.total_loss(); +} \ No newline at end of file diff --git a/server/src/client/voice/PacketStatistics.h b/server/src/client/voice/PacketStatistics.h new file mode 100644 index 0000000..44c2594 --- /dev/null +++ b/server/src/client/voice/PacketStatistics.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +namespace ts::server::client { + class PacketStatistics { + public: + struct PacketLossReport { + uint32_t lost_voice{0}; + uint32_t lost_control{0}; + uint32_t lost_keep_alive{0}; + + uint32_t received_voice{0}; + uint32_t received_control{0}; + uint32_t received_keep_alive{0}; + + [[nodiscard]] inline float voice_loss() const { + const auto total_packets = this->received_voice + this->lost_voice; + if(total_packets == 0) return 0; + return this->lost_voice / (float) total_packets; + } + [[nodiscard]] inline float control_loss() const { + const auto total_packets = this->received_control + this->lost_control; + //if(total_packets == 0) return 0; /* not possible so remove this to speed it up */ + return this->lost_control / (float) total_packets; + } + [[nodiscard]] inline float keep_alive_loss() const { + const auto total_packets = this->received_keep_alive + this->lost_keep_alive; + if(total_packets == 0) return 0; + return this->lost_keep_alive / (float) total_packets; + } + + [[nodiscard]] inline float total_loss() const { + const auto total_lost = this->lost_voice + this->lost_control + this->lost_keep_alive; + const auto total_received = this->received_control + this->received_voice + this->received_keep_alive; + //if(total_received + total_lost == 0) return 0; /* not possible to speed this up */ + return total_lost / (float) (total_lost + total_received); + } + }; + + [[nodiscard]] PacketLossReport loss_report() const; + [[nodiscard]] float current_packet_loss() const; + + void send_command(protocol::PacketType /* type */, uint32_t /* packet id */); + void received_acknowledge(protocol::PacketType /* type */, uint32_t /* packet id */); + + void received_packet(protocol::PacketType /* type */, uint32_t /* packet id */); + void tick(); + private: + std::chrono::system_clock::time_point last_short{}; + + spin_lock data_mutex{}; + protocol::UnorderedPacketLossCalculator calculator_voice_whisper{}; + protocol::UnorderedPacketLossCalculator calculator_voice{}; + + protocol::UnorderedPacketLossCalculator calculator_ack_low{}; + protocol::UnorderedPacketLossCalculator calculator_ack{}; + + protocol::UnorderedPacketLossCalculator calculator_ping{}; + + protocol::CommandPacketLossCalculator calculator_command{}; + protocol::CommandPacketLossCalculator calculator_command_low{}; + }; +} \ No newline at end of file diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index a4b89e0..eaf57e0 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -108,6 +108,8 @@ void VoiceClient::tick(const std::chrono::system_clock::time_point &time) { } else this->sendPingRequest(); } + + this->connection->packet_statistics().tick(); } else if(this->state == ConnectionState::INIT_LOW || this->state == ConnectionState::INIT_HIGH) { if(this->last_packet_handshake.time_since_epoch().count() != 0) { if(time - this->last_packet_handshake > seconds(5)) { @@ -309,4 +311,12 @@ void VoiceClient::send_voice_whisper_packet(const pipes::buffer_view &voice_buff memcpy(packet->data().data_ptr(), voice_buffer.data_ptr(), voice_buffer.length()); this->connection->sendPacket(packet, false, false); +} + +float VoiceClient::current_ping_deviation() { + return this->connection->getAcknowledgeManager().current_rttvar(); +} + +float VoiceClient::current_packet_loss() const { + return this->connection->packet_statistics().current_packet_loss(); } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClient.h b/server/src/client/voice/VoiceClient.h index 073706c..55d953c 100644 --- a/server/src/client/voice/VoiceClient.h +++ b/server/src/client/voice/VoiceClient.h @@ -63,7 +63,11 @@ namespace ts { connection::VoiceClientConnection* getConnection(){ return connection; } std::shared_ptr getVoiceServer(){ return voice_server; } - std::chrono::milliseconds calculatePing(){ return ping; } + + [[nodiscard]] inline std::chrono::milliseconds current_ping(){ return ping; } + [[nodiscard]] float current_ping_deviation(); + + [[nodiscard]] float current_packet_loss() const; private: connection::VoiceClientConnection* connection; diff --git a/server/src/client/voice/VoiceClientConnection.cpp b/server/src/client/voice/VoiceClientConnection.cpp index c94545c..eed6e7a 100644 --- a/server/src/client/voice/VoiceClientConnection.cpp +++ b/server/src/client/voice/VoiceClientConnection.cpp @@ -78,7 +78,6 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b } #endif #endif - ClientPacketParser packet_parser{buffer}; if(!packet_parser.valid()) { logTrace(this->client->getServerId(), "{} Received invalid packet. Dropping.", CLIENT_STR_LOG_PREFIX_(this->client)); @@ -87,6 +86,15 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b assert(packet_parser.type() >= 0 && packet_parser.type() < this->incoming_generation_estimators.size()); packet_parser.set_estimated_generation(this->incoming_generation_estimators[packet_parser.type()].visit_packet(packet_parser.packet_id())); + +#ifndef CONNECTION_NO_STATISTICS + if(this->client) { + auto stats = this->client->connectionStatistics; + stats->logIncomingPacket(stats::ConnectionStatistics::category::from_type(packet_parser.type()), buffer.length() + 96); /* 96 for the UDP packet overhead */ + } + this->packet_statistics().received_packet((protocol::PacketType) packet_parser.type(), packet_parser.full_packet_id()); +#endif + auto is_command = packet_parser.type() == protocol::COMMAND || packet_parser.type() == protocol::COMMAND_LOW; /* pretest if the packet is worth the effort of decoding it */ if(is_command) { @@ -168,11 +176,6 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b return; } -#ifndef CONNECTION_NO_STATISTICS - if(this->client && this->client->getServer()) - this->client->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::from_type(packet_parser.type()), buffer.length()); -#endif - #ifdef LOG_INCOMPING_PACKET_FRAGMENTS debugMessage(lstream << CLIENT_LOG_PREFIX << "Recived packet. PacketId: " << packet->packetId() << " PacketType: " << packet->type().name() << " Flags: " << packet->flags() << " - " << packet->data() << endl); #endif @@ -485,7 +488,11 @@ bool VoiceClientConnection::prepare_packet_for_write(vector &resu for(const auto& fragment : fragments) { if(!fragment->memory_state.id_branded) fragment->applyPacketId(this->packet_id_manager); + + if(fragment->type().type() == protocol::PacketType::COMMAND_LOW || fragment->type().type() == protocol::PacketType::COMMAND) + this->packet_statistics().send_command(fragment->type().type(), fragment->packetId() | fragment->generationId() << 16U); } + work_lock.unlock(); /* the rest could be unordered */ @@ -517,8 +524,10 @@ bool VoiceClientConnection::prepare_packet_for_write(vector &resu } #ifndef CONNECTION_NO_STATISTICS - if(statistics) - statistics->logOutgoingPacket(*fragment); + if(statistics) { + auto category = stats::ConnectionStatistics::category::from_type(fragment->type()); + statistics->logOutgoingPacket(category, fragment->length() + 96); /* 96 for the UDP packet overhead */ + } #endif this->acknowledge_handler.process_packet(*fragment); result.push_back(fragment->buffer()); @@ -584,7 +593,7 @@ int VoiceClientConnection::pop_write_buffer(pipes::buffer& target) { if(this->client->state == DISCONNECTED) return 2; - lock_guard write_queue_lock(this->write_queue_lock); + lock_guard wqlock{this->write_queue_lock}; size_t size = this->write_queue.size(); if(size == 0) return 2; diff --git a/server/src/client/voice/VoiceClientConnection.h b/server/src/client/voice/VoiceClientConnection.h index b1d9fb3..1135bbf 100644 --- a/server/src/client/voice/VoiceClientConnection.h +++ b/server/src/client/voice/VoiceClientConnection.h @@ -15,6 +15,7 @@ #include "VoiceClient.h" #include "protocol/AcknowledgeManager.h" #include +#include "./PacketStatistics.h" //#define LOG_ACK_SYSTEM #ifdef LOG_ACK_SYSTEM @@ -82,11 +83,14 @@ namespace ts { bool wait_empty_write_and_prepare_queue(std::chrono::time_point until = std::chrono::time_point()); protocol::PacketIdManager& getPacketIdManager() { return this->packet_id_manager; } + AcknowledgeManager& getAcknowledgeManager() { return this->acknowledge_handler; } inline auto& get_incoming_generation_estimators() { return this->incoming_generation_estimators; } void reset(); void force_insert_command(const pipes::buffer_view& /* payload */); void register_initiv_packet(); + + [[nodiscard]] inline auto& packet_statistics() { return this->packet_statistics_; } //buffer::SortedBufferQueue** getReadQueue() { return this->readTypedQueue; } protected: void handle_incoming_datagram(const pipes::buffer_view &buffer); @@ -175,6 +179,7 @@ namespace ts { return packet_index & 0x1U; /* use 0 for command and 1 for command low */ } + server::client::PacketStatistics packet_statistics_{}; }; } } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClientPacketHandler.cpp b/server/src/client/voice/VoiceClientPacketHandler.cpp index 589b0e3..1047a92 100644 --- a/server/src/client/voice/VoiceClientPacketHandler.cpp +++ b/server/src/client/voice/VoiceClientPacketHandler.cpp @@ -81,7 +81,12 @@ void VoiceClient::handlePacketVoice(const protocol::ClientPacketParser& packet) } void VoiceClient::handlePacketAck(const protocol::ClientPacketParser& packet) { + if(packet.payload_length() < 2) return; + uint16_t target_id{be2le16(packet.payload().data_ptr())}; + + this->connection->packet_statistics().received_acknowledge((protocol::PacketType) packet.type(), target_id | (packet.estimated_generation() << 16U)); + string error{}; - if(!this->connection->acknowledge_handler.process_acknowledge(packet.type(), packet.payload(), error)) + if(!this->connection->acknowledge_handler.process_acknowledge(packet.type(), target_id, error)) debugMessage(this->getServerId(), "{} Failed to handle acknowledge: {}", CLIENT_STR_LOG_PREFIX, error); } \ No newline at end of file diff --git a/server/src/server/VoiceServer.cpp b/server/src/server/VoiceServer.cpp index d33ebf1..1d2e857 100644 --- a/server/src/server/VoiceServer.cpp +++ b/server/src/server/VoiceServer.cpp @@ -148,7 +148,7 @@ void VoiceServer::execute_resend(const std::chrono::system_clock::time_point &no lock_guard lock(this->connectionLock); connections = this->activeConnections; } - deque buffers; + deque> buffers; string error; for(const auto& client : connections) { auto connection = client->getConnection(); @@ -165,9 +165,13 @@ void VoiceServer::execute_resend(const std::chrono::system_clock::time_point &no } else if(!buffers.empty()) { { lock_guard client_write_lock(connection->write_queue_lock); - connection->write_queue.insert(connection->write_queue.end(), buffers.begin(), buffers.end()); + for(auto& buf : buffers) + connection->write_queue.push_back(buf->buffer); } - //logTrace(client->getServerId(), "{} Resending {} packets.", CLIENT_STR_LOG_PREFIX_(client), buffers.size()); + for(auto& entry : buffers) + connection->packet_statistics().send_command((protocol::PacketType) entry->packet_type, entry->packet_id | entry->generation_id << 16U); + if(buffers.size() > 0) + logTrace(client->getServerId(), "{} Resending {} packets.", CLIENT_STR_LOG_PREFIX_(client), buffers.size()); buffers.clear(); connection->triggerWrite(); } diff --git a/shared b/shared index 607ae9a..e0eb4c5 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 607ae9a3e6a64c7ac1eb8c25b9cfab50b94f1d86 +Subproject commit e0eb4c5a16b900f6ed906c88a264d07479b13c5e