A lot of updates for 1.4.12
This commit is contained in:
parent
a2f52d98db
commit
eb61daab43
@ -1 +1 @@
|
||||
Subproject commit d4ffb41adc2fe5145be4ab343039e72f66058d33
|
||||
Subproject commit 136fac9ad2a22bf39327f77cd59f2f83a02b2eec
|
@ -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) {
|
||||
|
@ -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 "")
|
||||
|
@ -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 " {} {} | {}"
|
||||
|
@ -3,8 +3,9 @@
|
||||
//
|
||||
|
||||
#include <misc/memtracker.h>
|
||||
|
||||
#include <utility>
|
||||
#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<ConnectionStatistics>& handle, bool properties) : handle(handle) {
|
||||
ConnectionStatistics::ConnectionStatistics(shared_ptr<ConnectionStatistics> handle) : handle(std::move(handle)) {
|
||||
memtrack::allocated<ConnectionStatistics>(this);
|
||||
|
||||
if(properties) {
|
||||
this->properties = make_shared<Properties>(); //TODO load etc?
|
||||
this->properties->register_property_type<property::ConnectionProperties>();
|
||||
}
|
||||
|
||||
/*
|
||||
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<ConnectionStatistics>(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<Properties> 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<uint32_t> current{};
|
||||
current.atomic_exchange(this->statistics_second_current);
|
||||
|
||||
lock_guard lock(this->history_lock_incoming);
|
||||
auto period_ms = std::chrono::floor<std::chrono::milliseconds>(time_difference).count();
|
||||
auto current_normalized = current.mul<long double>(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<long double>(.2) + current_normalized.mul<long double>(.8);
|
||||
this->total_statistics += current;
|
||||
|
||||
this->history_incoming.pop_front();
|
||||
}
|
||||
auto current_second = std::chrono::floor<std::chrono::seconds>(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<uint32_t> ConnectionStatistics::minute_stats() const {
|
||||
BandwidthEntry<uint32_t> result{};
|
||||
for(const auto& second : this->statistics_minute)
|
||||
result += second;
|
||||
return result.mul<uint32_t>(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<uint64_t, uint64_t> ConnectionStatistics::mark_file_bytes() {
|
||||
std::pair<uint64_t, uint64_t> 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;
|
||||
|
@ -11,41 +11,97 @@ namespace ts {
|
||||
}
|
||||
|
||||
namespace stats {
|
||||
struct StatisticEntry {
|
||||
std::atomic<int8_t> use_count{0};
|
||||
std::chrono::time_point<std::chrono::system_clock> timestamp;
|
||||
uint16_t size = 0;
|
||||
template <typename value_t>
|
||||
struct BandwidthEntry {
|
||||
std::array<value_t, 3> connection_packets_sent{};
|
||||
std::array<value_t, 3> connection_bytes_sent{};
|
||||
std::array<value_t, 3> connection_packets_received{};
|
||||
std::array<value_t, 3> connection_bytes_received{};
|
||||
|
||||
value_t file_bytes_sent{0};
|
||||
value_t file_bytes_received{0};
|
||||
|
||||
template <typename other_type>
|
||||
inline BandwidthEntry& operator=(const BandwidthEntry<other_type>& 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 <typename target_t>
|
||||
inline BandwidthEntry<target_t> mul(double factor) const {
|
||||
BandwidthEntry<target_t> 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 <typename other_type>
|
||||
inline BandwidthEntry& operator+=(const BandwidthEntry<other_type>& 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 <typename other_type>
|
||||
inline BandwidthEntry operator+(const BandwidthEntry<other_type>& other) {
|
||||
return BandwidthEntry{*this} += other;
|
||||
}
|
||||
|
||||
template <typename atomic_t>
|
||||
inline void atomic_exchange(BandwidthEntry<std::atomic<atomic_t>>& 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<category::value, 16> 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<ConnectionStatistics>& /* root */, bool /* spawn properties */);
|
||||
explicit ConnectionStatistics(std::shared_ptr<ConnectionStatistics> /* root */);
|
||||
~ConnectionStatistics();
|
||||
|
||||
std::shared_ptr<Properties> 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<uint32_t>& total_stats() const { return this->total_statistics; }
|
||||
[[nodiscard]] inline BandwidthEntry<uint32_t> second_stats() const { return this->statistics_second; }
|
||||
[[nodiscard]] BandwidthEntry<uint32_t> minute_stats() const;
|
||||
|
||||
FileTransferStatistics file_stats();
|
||||
std::pair<uint64_t, uint64_t> 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<ConnectionStatistics> handle;
|
||||
std::shared_ptr<Properties> properties;
|
||||
|
||||
BandwidthEntry<uint32_t> total_statistics{};
|
||||
|
||||
std::atomic<uint64_t> connection_packets_sent[4]{0, 0, 0, 0};
|
||||
std::atomic<uint64_t> connection_bytes_sent[4]{0, 0, 0, 0};
|
||||
std::atomic<uint64_t> connection_packets_received[4]{0, 0, 0, 0};
|
||||
std::atomic<uint64_t> connection_bytes_received[4]{0, 0, 0, 0};
|
||||
BandwidthEntry<std::atomic<uint64_t>> statistics_second_current{};
|
||||
BandwidthEntry<uint32_t> statistics_second{}; /* will be updated every second by the stats from the "current_second" */
|
||||
std::array<BandwidthEntry<uint32_t>, 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<uint64_t> file_bytes_sent = 0;
|
||||
std::atomic<uint64_t> file_bytes_received = 0;
|
||||
std::atomic<uint64_t> file_bytes_sent{0};
|
||||
std::atomic<uint64_t> file_bytes_received{0};
|
||||
|
||||
std::atomic<uint64_t> mark_file_bytes_sent = 0;
|
||||
std::atomic<uint64_t> mark_file_bytes_received = 0;
|
||||
|
||||
spin_lock history_lock_outgoing;
|
||||
spin_lock history_lock_incoming;
|
||||
std::deque<StatisticEntry*> history_file_incoming{};
|
||||
std::deque<StatisticEntry*> history_file_outgoing{};
|
||||
std::deque<StatisticEntry*> history_incoming{};
|
||||
std::deque<StatisticEntry*> 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};
|
||||
};
|
||||
}
|
||||
}
|
@ -622,8 +622,8 @@ inline sql::result load_properties(ServerId sid, deque<unique_ptr<FastPropertyEn
|
||||
}
|
||||
}
|
||||
|
||||
const auto &info = property::impl::info_key(type, key);
|
||||
if(info->name == "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<unique_ptr<FastPropertyEn
|
||||
prop.setDbReference(true);
|
||||
*/
|
||||
|
||||
auto data = make_unique<FastPropertyEntry>();
|
||||
auto data = std::make_unique<FastPropertyEntry>();
|
||||
data->type = &info;
|
||||
data->value = value;
|
||||
data->type = info;
|
||||
properties.push_back(move(data));
|
||||
return 0;
|
||||
});
|
||||
@ -710,7 +710,7 @@ std::shared_ptr<Properties> 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<Properties> 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<Properties> 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<Properties> 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<StartupPropertyEntry>();
|
||||
entry->info = info;
|
||||
entry->info = &info;
|
||||
entry->value = value;
|
||||
entry->id = id;
|
||||
entry->type = type;
|
||||
|
@ -57,8 +57,8 @@ namespace ts {
|
||||
|
||||
struct StartupPropertyEntry {
|
||||
property::PropertyType type = property::PropertyType::PROP_TYPE_UNKNOWN;
|
||||
uint64_t id = 0;
|
||||
std::shared_ptr<property::PropertyDescription> 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<property::PropertyDescription> type;
|
||||
const property::PropertyDescription* type;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
|
@ -41,8 +41,7 @@ extern bool mainThreadActive;
|
||||
InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
|
||||
serverInstance = this;
|
||||
this->tick_manager = make_shared<threads::Scheduler>(config::threads::ticking, "tick task ");
|
||||
this->statistics = make_shared<stats::ConnectionStatistics>(nullptr, true);
|
||||
this->statistics->measure_bandwidths(true);
|
||||
this->statistics = make_shared<stats::ConnectionStatistics>(nullptr);
|
||||
|
||||
std::string error_message{};
|
||||
this->license_service_ = std::make_shared<license::LicenseService>();
|
||||
@ -70,8 +69,8 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
|
||||
}
|
||||
}
|
||||
|
||||
const auto &info = property::impl::info<property::InstanceProperties>(key);
|
||||
if(*info == property::SERVERINSTANCE_UNDEFINED) {
|
||||
const auto &info = property::find<property::InstanceProperties>(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));
|
||||
|
@ -134,8 +134,8 @@ bool InstanceHandler::setupDefaultGroups() {
|
||||
}
|
||||
|
||||
for(const auto& property : info->properties) {
|
||||
const auto& prop = property::impl::info<property::InstanceProperties>(property);
|
||||
if(*prop == property::SERVERINSTANCE_UNDEFINED) {
|
||||
const auto& prop = property::find<property::InstanceProperties>(property);
|
||||
if(prop.is_undefined()) {
|
||||
logCritical(LOG_INSTANCE, "Invalid template property name: " + property);
|
||||
} else {
|
||||
this->properties()[prop] = group->groupId();
|
||||
|
@ -201,7 +201,7 @@ std::shared_ptr<VirtualServer> 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<VirtualServer> VirtualServerManager::createServerFromSnapshot(sh
|
||||
if(key == "bot_owner_id") continue;
|
||||
if(key == "bot_id") continue;
|
||||
|
||||
const auto& property = property::info<property::ClientProperties>(key);
|
||||
if(property->property_index == property::CLIENT_UNDEFINED) {
|
||||
const auto& property = property::find<property::ClientProperties>(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<VirtualServer> VirtualServerManager::createServerFromSnapshot(sh
|
||||
if(key == "begin_playlist") continue;
|
||||
if(key == "playlist_id") continue;
|
||||
|
||||
const auto& property = property::info<property::ClientProperties>(key);
|
||||
if(property->property_index == property::CLIENT_UNDEFINED) {
|
||||
const auto& property = property::find<property::ClientProperties>(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<Virtual
|
||||
case property::VIRTUALSERVER_UPLOAD_QUOTA:
|
||||
case property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH:
|
||||
case property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH:
|
||||
cmd[index][serverProperty.type().name] = (uint64_t) serverProperty.as_save<int64_t>();
|
||||
cmd[index][std::string{serverProperty.type().name}] = (uint64_t) serverProperty.as_save<int64_t>();
|
||||
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<Virtual
|
||||
else if(channelProperty.type() == property::CHANNEL_PID)
|
||||
cmd[index]["channel_pid"] = channelProperty.as<string>();
|
||||
else
|
||||
cmd[index][channelProperty.type().name] = channelProperty.as<string>();
|
||||
cmd[index][std::string{channelProperty.type().name}] = channelProperty.as<string>();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@ -899,7 +899,7 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr<Virtual
|
||||
if((property->type->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_ptr<Virtual
|
||||
if((property->type->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++;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ bool VirtualServer::initialize(bool test_properties) {
|
||||
|
||||
letters = new letter::LetterManager(this);
|
||||
|
||||
serverStatistics = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics(), true);
|
||||
serverStatistics = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics());
|
||||
|
||||
this->serverRoot = std::make_shared<InternalClient>(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as<string>(), false);
|
||||
static_pointer_cast<InternalClient>(this->serverRoot)->setSharedLock(this->serverRoot);
|
||||
@ -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<ConnectedClient> invoker,
|
||||
cmd["invokeruid"] = invoker->getUid();
|
||||
cmd["reasonid"] = ViewReasonId::VREASON_EDITED;
|
||||
for(const auto& key : keys) {
|
||||
auto info = property::impl::info<property::VirtualServerProperties>(key);
|
||||
if(*info == property::VIRTUALSERVER_UNDEFINED) {
|
||||
const auto& info = property::find<property::VirtualServerProperties>(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<ConnectedClient> invoker,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient> client, const deque<shared_ptr<property::PropertyDescription>>& keys, bool selfNotify) {
|
||||
bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient> client, const deque<const property::PropertyDescription*>& keys, bool selfNotify) {
|
||||
if(keys.empty()) return false;
|
||||
this->forEachClient([&](const shared_ptr<ConnectedClient>& 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<std::string>();
|
||||
}
|
||||
|
||||
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<ConnectedClient> client) {
|
||||
auto type = client->getType();
|
||||
if(type == ClientType::CLIENT_TEAMSPEAK || type == ClientType::CLIENT_TEASPEAK) {
|
||||
count++;
|
||||
sum += duration_cast<milliseconds>(dynamic_pointer_cast<VoiceClient>(client)->calculatePing()).count();
|
||||
this->forEachClient([&](const std::shared_ptr<ConnectedClient>& client) {
|
||||
if(auto vc = dynamic_pointer_cast<VoiceClient>(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<milliseconds>(dynamic_pointer_cast<WebClient>(client)->client_ping()).count();
|
||||
else if(client->getType() == ClientType::CLIENT_WEB) {
|
||||
pings_counted++;
|
||||
total_ping += duration_cast<milliseconds>(dynamic_pointer_cast<WebClient>(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) {
|
||||
|
@ -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<ConnectedClient>, std::deque<std::string> keys);
|
||||
bool notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient>, const std::deque<std::shared_ptr<property::PropertyDescription>>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */
|
||||
bool notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient>, const std::deque<const property::PropertyDescription*>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */
|
||||
inline bool notifyClientPropertyUpdates(const std::shared_ptr<ConnectedClient>& client, const std::deque<property::ClientProperties>& keys, bool selfNotify = true) {
|
||||
if(keys.empty()) return false;
|
||||
std::deque<std::shared_ptr<property::PropertyDescription>> _keys;
|
||||
for(const auto& key : keys) _keys.push_back(property::impl::info<property::ClientProperties>(key));
|
||||
std::deque<const property::PropertyDescription*> _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<ConnectedClient>& invoker);
|
||||
|
||||
float averagePing();
|
||||
float averagePacketLoss();
|
||||
[[nodiscard]] NetworkReport generate_network_report();
|
||||
|
||||
bool resetPermissions(std::string&);
|
||||
void ensureValidDefaultGroups();
|
||||
|
@ -29,9 +29,7 @@ ConnectedClient::ConnectedClient(sql::SqlManager* db, const std::shared_ptr<Virt
|
||||
memtrack::allocated<ConnectedClient>(this);
|
||||
memset(&this->remote_address, 0, sizeof(this->remote_address));
|
||||
|
||||
connectionStatistics = make_shared<stats::ConnectionStatistics>(server ? server->getServerStatistics() : nullptr, false);
|
||||
this->connectionStatistics->measure_bandwidths(false); /* done by the client and we trust this */
|
||||
|
||||
connectionStatistics = make_shared<stats::ConnectionStatistics>(server ? server->getServerStatistics() : nullptr);
|
||||
channels = make_shared<ClientChannelView>(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;
|
||||
|
||||
|
@ -135,7 +135,7 @@ namespace ts {
|
||||
virtual bool notifyClientPoke(std::shared_ptr<ConnectedClient> invoker, std::string msg);
|
||||
virtual bool notifyClientUpdated(
|
||||
const std::shared_ptr<ConnectedClient> &,
|
||||
const std::deque<std::shared_ptr<property::PropertyDescription>> &,
|
||||
const std::deque<const property::PropertyDescription*> &,
|
||||
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;
|
||||
|
@ -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<ConnectedClient> &target, const shared_ptr<ConnectionInfoData> &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<VoiceClient>(target))
|
||||
notify["connection_ping"] = floor<milliseconds>(dynamic_pointer_cast<VoiceClient>(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<VoiceClient>(target); vc) {
|
||||
bulk.put(property::CONNECTION_PING, floor<milliseconds>(vc->current_ping()).count());
|
||||
bulk.put(property::CONNECTION_PING_DEVIATION, vc->current_ping_deviation());
|
||||
}
|
||||
#ifdef COMPILE_WEB_CLIENT
|
||||
else if(dynamic_pointer_cast<WebClient>(target))
|
||||
notify["connection_ping"] = floor<milliseconds>(dynamic_pointer_cast<WebClient>(target)->client_ping()).count();
|
||||
else if(dynamic_pointer_cast<WebClient>(target))
|
||||
bulk.put(property::CONNECTION_PING, floor<milliseconds>(dynamic_pointer_cast<WebClient>(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<VoiceClient>(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::milliseconds>(chrono::system_clock::now() - target->connectTimestamp).count();
|
||||
notify["connection_idle_time"] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - target->idleTimestamp).count();
|
||||
bulk.put(property::CONNECTION_CONNECTED_TIME, chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - target->connectTimestamp).count());
|
||||
bulk.put(property::CONNECTION_IDLE_TIME, chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - target->idleTimestamp).count());
|
||||
this->sendCommand(notify);
|
||||
return true;
|
||||
}
|
||||
@ -404,7 +412,7 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr<ConnectedClient> &clien
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &client, const deque<shared_ptr<property::PropertyDescription>> &props, bool lock) {
|
||||
bool ConnectedClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &client, const deque<const property::PropertyDescription*> &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<ConnectedClient>
|
||||
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<int64_t>() + duration_cast<seconds>(system_clock::now() - client->lastOnlineTimestamp).count();
|
||||
else
|
||||
response[prop->name] = client->properties()[prop].value();
|
||||
@ -650,7 +658,7 @@ bool ConnectedClient::notifyClientEnterView(const std::deque<std::shared_ptr<Con
|
||||
|
||||
this->visibleClients.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<string>();
|
||||
notify[prop_info.name] = channel->properties()[prop].as<string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::PropertyDescription> &property_info = property::info<property::ClientProperties>(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<property::ClientProperties>(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::PropertyDescription> &property_info = property::info<property::PlaylistProperties>(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<property::PlaylistProperties>(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::PropertyDescription> &property_info = property::info<property::PlaylistProperties>(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<property::PlaylistProperties>(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<VoiceClient>(_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);
|
||||
|
||||
|
@ -45,10 +45,17 @@ namespace ts {
|
||||
return std::forward<F>(f)(*handle);
|
||||
}
|
||||
|
||||
/*
|
||||
template <typename T>
|
||||
ts::PropertyWrapper operator[](T type) {
|
||||
return (*handle)[type];
|
||||
}
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
ts::PropertyWrapper operator[](const T& type) {
|
||||
return (*handle)[type];
|
||||
}
|
||||
};
|
||||
|
||||
DataClient(sql::SqlManager*, const std::shared_ptr<VirtualServer>&);
|
||||
|
@ -479,13 +479,13 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
const auto &info = property::info<property::ClientProperties>(key);
|
||||
if(*info == property::CLIENT_UNDEFINED) {
|
||||
const auto &info = property::find<property::ClientProperties>(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<string>()))
|
||||
if(!info.validate_input(cmd[key].as<string>()))
|
||||
return command_result{error::parameter_invalid};
|
||||
|
||||
this->properties()[info] = cmd[key].as<std::string>();
|
||||
|
@ -737,14 +737,14 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
|
||||
if (prop == "cpid") continue;
|
||||
if (prop == "cid") continue;
|
||||
|
||||
const auto &property = property::info<property::ChannelProperties>(prop);
|
||||
if(*property == property::CHANNEL_UNDEFINED) {
|
||||
const auto &property = property::find<property::ChannelProperties>(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<string>())) {
|
||||
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as<string>() + "', Property: '" + property->name + "')");
|
||||
if(!property.validate_input(cmd[prop].as<string>())) {
|
||||
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as<string>() + "', Property: '" + std::string{property.name} + "')");
|
||||
continue;
|
||||
}
|
||||
created_channel->properties()[property] = cmd[prop].as<std::string>();
|
||||
@ -847,7 +847,7 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<property::PropertyDescription>> keys;
|
||||
std::deque<const property::PropertyDescription*> 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<property::ChannelProperties>(key);
|
||||
if(*property == property::CHANNEL_UNDEFINED) {
|
||||
const auto &property = property::find<property::ChannelProperties>(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<string>())) {
|
||||
if(!property.validate_input(cmd[key].as<string>())) {
|
||||
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<string>() == cmd[key].as<string>())
|
||||
if(channel->properties()[property].as<string>() == cmd[key].as<string>())
|
||||
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::ChannelProperties>(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::ChannelProperties>(property::CHANNEL_FLAG_PASSWORD));
|
||||
keys.push_back(&property::describe(property::CHANNEL_FLAG_PASSWORD));
|
||||
}
|
||||
|
||||
if(cmd["channel_flag_password"].as<bool>()) {
|
||||
@ -1019,20 +1019,20 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
|
||||
if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as<bool>()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) {
|
||||
cmd["channel_flag_password"] = false;
|
||||
cmd["channel_password"] = "";
|
||||
keys.push_back(property::info<property::ChannelProperties>(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::ChannelProperties>(property::CHANNEL_MAXCLIENTS));
|
||||
keys.push_back(property::info<property::ChannelProperties>(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::ChannelProperties>(property::CHANNEL_MAXFAMILYCLIENTS));
|
||||
keys.push_back(property::info<property::ChannelProperties>(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<int>() != -1) {
|
||||
cmd["channel_maxclients"] = -1;
|
||||
cmd["channel_flag_maxclients_unlimited"] = true;
|
||||
keys.push_back(property::info<property::ChannelProperties>(property::CHANNEL_MAXCLIENTS));
|
||||
keys.push_back(property::info<property::ChannelProperties>(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<int>() != -1) {
|
||||
cmd["channel_maxfamilyclients"] = -1;
|
||||
cmd["channel_flag_maxfamilyclients_inherited"] = true;
|
||||
keys.push_back(property::info<property::ChannelProperties>(property::CHANNEL_MAXFAMILYCLIENTS));
|
||||
keys.push_back(property::info<property::ChannelProperties>(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::ChannelProperties>(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<int>() < 0;
|
||||
keys.push_back(property::info<property::ChannelProperties>(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED));
|
||||
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED));
|
||||
}
|
||||
|
||||
if(cmd["channel_flag_maxclients_unlimited"].as<bool>() && cmd["channel_maxclients"].as<int>() != -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::ChannelProperties>(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<bool>())
|
||||
cmd["channel_flag_maxfamilyclients_unlimited"] = false;
|
||||
else
|
||||
cmd["channel_flag_maxfamilyclients_unlimited"] = true;
|
||||
keys.push_back(property::info<property::ChannelProperties>(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::ChannelProperties>(property::CHANNEL_MAXFAMILYCLIENTS));
|
||||
keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS));
|
||||
}
|
||||
//keep this order because this command: "channeledit cid=<x> 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<BasicChannel> old_default_channel;
|
||||
deque<shared_ptr<BasicChannel>> child_channel_updated;
|
||||
for(const std::shared_ptr<property::PropertyDescription>& 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<bool>()) {
|
||||
if(!cmd[std::string{key->name}].as<bool>()) {
|
||||
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<int>(), 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<property::ChannelProperties> key_vector;
|
||||
|
@ -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<shared_ptr<property::PropertyDescription>> props;
|
||||
deque<const property::PropertyDescription*> 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<property::ClientProperties>(elm);
|
||||
if(*info == property::CLIENT_UNDEFINED) {
|
||||
const auto& info = property::find<property::ClientProperties>(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<string>())) {
|
||||
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as<string>() + "', Property: '" + info->name + "')");
|
||||
if(!info.validate_input(cmd[elm].as<string>())) {
|
||||
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as<string>() + "', 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<lock_guard<std::recursive_mutex>> nickname_lock;
|
||||
deque<pair<property::ClientProperties, string>> keys;
|
||||
std::deque<std::pair<const property::PropertyDescription*, std::string>> keys;
|
||||
for(const auto& key : cmd[0].keys()) {
|
||||
if(key == "return_code") continue;
|
||||
if(key == "clid") continue;
|
||||
|
||||
const auto &info = property::info<property::ClientProperties>(key);
|
||||
if(*info == property::CLIENT_UNDEFINED) {
|
||||
const auto &info = property::find<property::ClientProperties>(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<string>())) {
|
||||
if(!info.validate_input(cmd[key].as<string>())) {
|
||||
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<string>() == cmd[key].as<string>()) continue;
|
||||
if(client->properties()[&info].as<string>() == cmd[key].as<string>()) 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<bool>();
|
||||
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<bool>())
|
||||
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<seconds>(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<MusicClient::Type::value>();
|
||||
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<property::ClientProperties> updates;
|
||||
deque<const property::PropertyDescription*> 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<bool>() ? 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<string>();
|
||||
else
|
||||
|
@ -366,7 +366,7 @@ command_result ConnectedClient::handleCommandPermissionList(Command &cmd) {
|
||||
|
||||
#define M(ptype) \
|
||||
do { \
|
||||
for(const auto& prop : property::impl::list<ptype>()) { \
|
||||
for(const auto& prop : property::list<ptype>()) { \
|
||||
if((prop->flags & property::FLAG_INTERNAL) > 0) continue; \
|
||||
response[index]["name"] = prop->name; \
|
||||
response[index]["flags"] = prop->flags; \
|
||||
|
@ -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<pair<shared_ptr<property::PropertyDescription>, string>> properties;
|
||||
deque<pair<const property::PropertyDescription*, string>> properties;
|
||||
|
||||
for(const auto& key : cmd[0].keys()) {
|
||||
if(key == "playlist_id") continue;
|
||||
if(key == "return_code") continue;
|
||||
|
||||
auto property = property::info<property::PlaylistProperties>(key);
|
||||
if(*property == property::PLAYLIST_UNDEFINED) {
|
||||
const auto& property = property::find<property::PlaylistProperties>(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<string>())) {
|
||||
if(!property.validate_input(cmd[key].as<string>())) {
|
||||
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<SongId>();
|
||||
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<int32_t>();
|
||||
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) {
|
||||
|
@ -224,14 +224,14 @@ command_result ConnectedClient::handleCommandServerEdit(Command &cmd) {
|
||||
std::deque<std::string> keys;
|
||||
bool group_update = false;
|
||||
for (const auto& elm : toApplay) {
|
||||
auto info = property::impl::info<property::VirtualServerProperties>(elm.first);
|
||||
if(*info == property::VIRTUALSERVER_UNDEFINED) {
|
||||
const auto& info = property::find<property::VirtualServerProperties>(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<string>();
|
||||
notify[0]["connection_filetransfer_bytes_received_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
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<string>();
|
||||
notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as<string>();
|
||||
first_bulk.put_unchecked("connection_filetransfer_bytes_sent_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as<string>());
|
||||
first_bulk.put_unchecked("connection_filetransfer_bytes_received_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as<string>());
|
||||
|
||||
notify[0]["connection_packets_sent_total"] = (*statistics)[property::CONNECTION_PACKETS_SENT_TOTAL].as<string>();
|
||||
notify[0]["connection_bytes_sent_total"] = (*statistics)[property::CONNECTION_BYTES_SENT_TOTAL].as<string>();
|
||||
notify[0]["connection_packets_received_total"] = (*statistics)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as<string>();
|
||||
notify[0]["connection_bytes_received_total"] = (*statistics)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
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<string>();
|
||||
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<string>());
|
||||
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};
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ namespace ts::server {
|
||||
bool notifyServerUpdated(std::shared_ptr<ConnectedClient> ptr) override;
|
||||
bool notifyClientPoke(std::shared_ptr<ConnectedClient> invoker, std::string msg) override;
|
||||
|
||||
bool notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<std::shared_ptr<property::PropertyDescription>> &deque, bool lock_channel_tree) override;
|
||||
bool notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<const property::PropertyDescription*> &deque, bool lock_channel_tree) override;
|
||||
|
||||
bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr<ConnectedClient>) override;
|
||||
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <misc/base64.h>
|
||||
#include <src/ShutdownHelper.h>
|
||||
#include <ThreadPool/Timer.h>
|
||||
#include <numeric>
|
||||
|
||||
#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<string>();
|
||||
cmd["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
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<property::VirtualServerProperties>(key);
|
||||
if(*info == property::VIRTUALSERVER_UNDEFINED) {
|
||||
const auto& info = property::find<property::VirtualServerProperties>(key);
|
||||
if(info == property::VIRTUALSERVER_UNDEFINED) {
|
||||
logError(server->getServerId(), "Tried to change unknown server property " + key);
|
||||
continue;
|
||||
}
|
||||
if(!info->validate_input(cmd[key].as<string>())) {
|
||||
if(!info.validate_input(cmd[key].as<string>())) {
|
||||
logError(server->getServerId(), "Tried to change " + key + " to an invalid value: " + cmd[key].as<string>());
|
||||
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<property::InstanceProperties>(key);
|
||||
const auto* info = &property::find<property::InstanceProperties>(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<string>();
|
||||
res["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].as<string>();
|
||||
res["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as<string>();
|
||||
res["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
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<string>();
|
||||
res["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
|
||||
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};
|
||||
|
@ -42,7 +42,7 @@ bool QueryClient::notifyServerUpdated(shared_ptr<ConnectedClient> ptr) {
|
||||
return ConnectedClient::notifyServerUpdated(ptr);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<std::shared_ptr<property::PropertyDescription>> &deque, bool lock_channel_tree) {
|
||||
bool QueryClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<const property::PropertyDescription*> &deque, bool lock_channel_tree) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE);
|
||||
return ConnectedClient::notifyClientUpdated(ptr, deque, lock_channel_tree);
|
||||
}
|
||||
|
95
server/src/client/voice/PacketStatistics.cpp
Normal file
95
server/src/client/voice/PacketStatistics.cpp
Normal file
@ -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();
|
||||
}
|
66
server/src/client/voice/PacketStatistics.h
Normal file
66
server/src/client/voice/PacketStatistics.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <protocol/PacketLossCalculator.h>
|
||||
#include <protocol/Packet.h>
|
||||
#include <misc/spin_lock.h>
|
||||
|
||||
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{};
|
||||
};
|
||||
}
|
@ -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<void>(), voice_buffer.data_ptr<void>(), 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();
|
||||
}
|
@ -63,7 +63,11 @@ namespace ts {
|
||||
|
||||
connection::VoiceClientConnection* getConnection(){ return connection; }
|
||||
std::shared_ptr<VoiceServer> 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;
|
||||
|
||||
|
@ -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<pipes::buffer> &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<pipes::buffer> &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;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "VoiceClient.h"
|
||||
#include "protocol/AcknowledgeManager.h"
|
||||
#include <protocol/generation.h>
|
||||
#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<std::chrono::system_clock> until = std::chrono::time_point<std::chrono::system_clock>());
|
||||
|
||||
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<protocol::ClientPacket>** 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_{};
|
||||
};
|
||||
}
|
||||
}
|
@ -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<char>())};
|
||||
|
||||
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);
|
||||
}
|
@ -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<pipes::buffer> buffers;
|
||||
deque<std::shared_ptr<connection::AcknowledgeManager::Entry>> 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();
|
||||
}
|
||||
|
2
shared
2
shared
@ -1 +1 @@
|
||||
Subproject commit 607ae9a3e6a64c7ac1eb8c25b9cfab50b94f1d86
|
||||
Subproject commit e0eb4c5a16b900f6ed906c88a264d07479b13c5e
|
Loading…
Reference in New Issue
Block a user