diff --git a/modules/renderer/connection/ServerConnection.ts b/modules/renderer/connection/ServerConnection.ts index 5fdc196..5f69b12 100644 --- a/modules/renderer/connection/ServerConnection.ts +++ b/modules/renderer/connection/ServerConnection.ts @@ -3,7 +3,7 @@ import { AbstractServerConnection, CommandOptionDefaults, CommandOptions, - ConnectionStateListener, ConnectionStatistics, + ConnectionStatistics, ServerCommand } from "tc-shared/connection/ConnectionBase"; import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; @@ -353,9 +353,11 @@ export class ServerConnection extends AbstractServerConnection { } getControlStatistics(): ConnectionStatistics { + const stats = this.nativeHandle?.statistics(); + return { - bytesReceived: 0, - bytesSend: 0 + bytesReceived: stats?.control_bytes_received ? stats?.control_bytes_received : 0, + bytesSend: stats?.control_bytes_send ? stats?.control_bytes_send : 0 }; } diff --git a/modules/renderer/connection/VoiceConnection.ts b/modules/renderer/connection/VoiceConnection.ts index 0ed4d6d..fb547bb 100644 --- a/modules/renderer/connection/VoiceConnection.ts +++ b/modules/renderer/connection/VoiceConnection.ts @@ -4,7 +4,7 @@ import { WhisperSessionInitializer } from "tc-shared/connection/VoiceConnection"; import {RecorderProfile} from "tc-shared/voice/RecorderProfile"; -import {NativeVoiceClient, NativeVoiceConnection, PlayerState} from "tc-native/connection"; +import {NativeServerConnection, NativeVoiceClient, NativeVoiceConnection, PlayerState} from "tc-native/connection"; import {ServerConnection} from "./ServerConnection"; import {VoiceClient} from "tc-shared/voice/VoiceClient"; import {WhisperSession, WhisperTarget} from "tc-shared/voice/VoiceWhisper"; @@ -231,9 +231,12 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection { } getConnectionStats(): Promise { + /* FIXME: This is iffy! */ + const stats = (this.connection as any as NativeServerConnection)["nativeHandle"]?.statistics(); + return Promise.resolve({ - bytesSend: 0, - bytesReceived: 0 + bytesSend: stats?.voice_bytes_send ? stats?.voice_bytes_send : 0, + bytesReceived: stats?.voice_bytes_received ? stats?.voice_bytes_received : 0 }); } diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index d3dfe26..73d17e0 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -22,7 +22,7 @@ message("Module path: ${CMAKE_MODULE_PATH}") function(setup_nodejs) set(NodeJS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") set(NODEJS_URL "https://atom.io/download/atom-shell") - set(NODEJS_VERSION "v11.0.3") + set(NODEJS_VERSION "v8.0.0") #set(NODEJS_URL "https://nodejs.org/download/release/") #set(NODEJS_VERSION "v12.13.0") diff --git a/native/serverconnection/exports/exports.d.ts b/native/serverconnection/exports/exports.d.ts index 8635121..5a3417e 100644 --- a/native/serverconnection/exports/exports.d.ts +++ b/native/serverconnection/exports/exports.d.ts @@ -78,6 +78,8 @@ declare module "tc-native/connection" { /* ping in microseconds */ current_ping() : number; + + statistics() : { voice_bytes_received: number, voice_bytes_send: number, control_bytes_received: number, control_bytes_send } | undefined } export function spawn_server_connection() : NativeServerConnection; diff --git a/native/serverconnection/src/connection/ProtocolHandler.cpp b/native/serverconnection/src/connection/ProtocolHandler.cpp index 0ee7d1e..42ca92d 100644 --- a/native/serverconnection/src/connection/ProtocolHandler.cpp +++ b/native/serverconnection/src/connection/ProtocolHandler.cpp @@ -67,6 +67,11 @@ void ProtocolHandler::reset() { this->crypt_handler.reset(); this->ping.ping_received_timestamp = system_clock::time_point{}; + + this->statistics_.control_bytes_received = 0; + this->statistics_.control_bytes_send = 0; + this->statistics_.voice_bytes_send = 0; + this->statistics_.voice_bytes_received = 0; } void ProtocolHandler::connect() { @@ -125,8 +130,9 @@ void ProtocolHandler::execute_tick() { } void ProtocolHandler::execute_resend() { - if(this->connection_state >= connection_state::DISCONNECTED) + if(this->connection_state >= connection_state::DISCONNECTED) { return; + } std::deque> buffers; auto now = system_clock::now(); @@ -145,8 +151,12 @@ void ProtocolHandler::execute_resend() { auto socket = this->handle->get_socket(); if(socket) { - for(const auto& buffer : buffers) + for(const auto& buffer : buffers) { socket->send_message(buffer->buffer); + + /* only control packets are getting resend */ + this->statistics_.control_bytes_send += buffer->buffer.length(); + } } this->handle->schedule_resend(next); @@ -169,11 +179,17 @@ void ProtocolHandler::progress_packet(const pipes::buffer_view &buffer) { auto ordered = packet_type.type() == protocol::COMMAND || packet_type.type() == protocol::COMMAND_LOW; //log_trace(category::connection, tr("Received packet {} with id {}"), packet->type().name(), packet->packetId()); - /* - if(ordered) - if(rand() & 1) - return; - */ + switch(packet_type.type()) { + case ts::protocol::PacketType::VOICE: + case ts::protocol::PacketType::VOICE_WHISPER: + this->statistics_.voice_bytes_received += buffer.length(); + break; + + case ts::protocol::PacketType::COMMAND: + case ts::protocol::PacketType::COMMAND_LOW: + this->statistics_.control_bytes_received += buffer.length(); + break; + } /* special handling */ if(packet_type.type() == protocol::INIT1) { @@ -565,6 +581,7 @@ void ProtocolHandler::send_command(const ts::Command &cmd, const std::function(end - begin).count(), f); }); } + packet->enable_flag(PacketFlag::NewProtocol); this->send_packet(packet); } @@ -594,8 +611,24 @@ void ProtocolHandler::send_packet(const std::shared_ptrsend_message(buffer); + + total_size += buffer.length(); + } + + switch(packet->type().type()) { + case ts::protocol::PacketType::VOICE: + case ts::protocol::PacketType::VOICE_WHISPER: + this->statistics_.voice_bytes_send += total_size; + break; + + case ts::protocol::PacketType::COMMAND: + case ts::protocol::PacketType::COMMAND_LOW: + this->statistics_.control_bytes_send += total_size; + break; + } } void ProtocolHandler::send_acknowledge(uint16_t packet_id, bool low) { @@ -632,4 +665,8 @@ void ProtocolHandler::disconnect(const std::string &reason) { if(success && this->connection_state == connection_state::DISCONNECTING && this->disconnect_id == did) this->handle->close_connection(); }); +} + +const ConnectionStatistics& ProtocolHandler::statistics() { + return this->statistics_; } \ No newline at end of file diff --git a/native/serverconnection/src/connection/ProtocolHandler.h b/native/serverconnection/src/connection/ProtocolHandler.h index 2a7860d..2501a7a 100644 --- a/native/serverconnection/src/connection/ProtocolHandler.h +++ b/native/serverconnection/src/connection/ProtocolHandler.h @@ -16,139 +16,147 @@ #include #include "ServerConnection.h" -namespace ts { - namespace connection { - class CryptionHandler; - class CompressionHandler; - } +namespace ts::connection { + class CryptionHandler; + class CompressionHandler; } -namespace tc { - namespace connection { - class ServerConnection; +namespace tc::connection { + class ServerConnection; - namespace connection_state { - enum value { - INITIALIZING, - INIT_LOW, - INIT_HIGH, - CONNECTING, - CONNECTED, - DISCONNECTING, - DISCONNECTED - }; - }; - namespace pow_state { - enum value : uint8_t { - COOKIE_GET, - COOKIE_SET, - PUZZLE_GET, - PUZZLE_SET, - PUZZLE_SOLVE, - PUZZLE_RESET, - COMPLETED, - COMMAND_RESET = 127, - UNSET = 0xFB - }; - }; + namespace connection_state { + enum value { + INITIALIZING, + INIT_LOW, + INIT_HIGH, + CONNECTING, + CONNECTED, + DISCONNECTING, + DISCONNECTED + }; + }; + namespace pow_state { + enum value : uint8_t { + COOKIE_GET, + COOKIE_SET, + PUZZLE_GET, + PUZZLE_SET, + PUZZLE_SOLVE, + PUZZLE_RESET, + COMPLETED, + COMMAND_RESET = 127, + UNSET = 0xFB + }; + }; - class ProtocolHandler { - typedef ts::protocol::PacketRingBuffer packet_buffer_t; - typedef std::array packet_buffers_t; - friend class ServerConnection; - public: - ProtocolHandler(ServerConnection*); - ~ProtocolHandler(); + struct ConnectionStatistics { + size_t control_bytes_send{0}; + size_t control_bytes_received{0}; - void reset(); - void connect(); - void execute_tick(); - void execute_resend(); + size_t voice_bytes_send{0}; + size_t voice_bytes_received{0}; + }; - void progress_packet(const pipes::buffer_view& /* buffer */); - bool handle_packets(); /* if true we have more left */ - void send_packet(const std::shared_ptr& /* packet */); - void send_command(const ts::Command& /* command */, const std::function & /* acknowledge callback */ = NULL); + class ProtocolHandler { + typedef ts::protocol::PacketRingBuffer packet_buffer_t; + typedef std::array packet_buffers_t; + friend class ServerConnection; + public: + explicit ProtocolHandler(ServerConnection*); + ~ProtocolHandler(); - void disconnect(const std::string& /* message */); + void reset(); + void connect(); + void execute_tick(); + void execute_resend(); - void send_acknowledge(uint16_t /* packet id */, bool /* low */); + const ConnectionStatistics& statistics(); - ecc_key& get_identity_key() { return this->crypto.identity; } + void progress_packet(const pipes::buffer_view& /* buffer */); + bool handle_packets(); /* if true we have more left */ + void send_packet(const std::shared_ptr& /* packet */); + void send_command(const ts::Command& /* command */, const std::function & /* acknowledge callback */ = NULL); - inline std::chrono::microseconds current_ping() { return this->ping.value; } + void disconnect(const std::string& /* message */); + void send_acknowledge(uint16_t /* packet id */, bool /* low */); - connection_state::value connection_state = connection_state::INITIALIZING; - server_type::value server_type = server_type::TEASPEAK; - private: - void do_close_connection(); /* only call from ServerConnection. Close all connections via ServerConnection! */ + ecc_key& get_identity_key() { return this->crypto.identity; } - void handlePacketCommand(const std::shared_ptr&); - void handlePacketAck(const std::shared_ptr&); - void handlePacketVoice(const std::shared_ptr&); - void handlePacketPing(const std::shared_ptr&); - void handlePacketInit(const std::shared_ptr&); + inline std::chrono::microseconds current_ping() { return this->ping.value; } - bool create_datagram_packets(std::vector &result, const std::shared_ptr &packet); + connection_state::value connection_state = connection_state::INITIALIZING; + server_type::value server_type = server_type::TEASPEAK; + private: + void do_close_connection(); /* only call from ServerConnection. Close all connections via ServerConnection! */ - ServerConnection* handle; + void handlePacketCommand(const std::shared_ptr&); + void handlePacketAck(const std::shared_ptr&); + void handlePacketVoice(const std::shared_ptr&); + void handlePacketPing(const std::shared_ptr&); + void handlePacketInit(const std::shared_ptr&); - std::chrono::system_clock::time_point connect_timestamp; - std::chrono::system_clock::time_point disconnect_timestamp; - uint8_t disconnect_id = 0; + bool create_datagram_packets(std::vector &result, const std::shared_ptr &packet); - struct { - size_t retry_count{0}; - pow_state::value state; + ServerConnection* handle; - uint64_t client_ts3_build_timestamp = 173265950 /* TS3 */; /* needs to be lower than 173265950 for old stuff, else new protocol */ - uint8_t client_control_data[4] = {0,0,0,0}; - uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - uint8_t server_data[100]; + std::chrono::system_clock::time_point connect_timestamp; + std::chrono::system_clock::time_point disconnect_timestamp; + uint8_t disconnect_id = 0; - std::chrono::system_clock::time_point last_response; - std::chrono::system_clock::time_point last_resend; - pipes::buffer last_buffer; - } pow; - void pow_send_cookie_get(); + struct { + size_t retry_count{0}; + pow_state::value state; - struct { - uint8_t alpha[10]; - uint8_t beta[54]; - uint8_t beta_length; /* 10 or 54 */ + uint64_t client_ts3_build_timestamp = 173265950 /* TS3 */; /* needs to be lower than 173265950 for old stuff, else new protocol */ + uint8_t client_control_data[4] = {0,0,0,0}; + uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + uint8_t server_data[100]; - ecc_key identity{}; + std::chrono::system_clock::time_point last_response; + std::chrono::system_clock::time_point last_resend; + pipes::buffer last_buffer; + } pow; + void pow_send_cookie_get(); - std::string initiv_command; - } crypto; - std::string generate_client_initiv(); + struct { + uint8_t alpha[10]; + uint8_t beta[54]; + uint8_t beta_length; /* 10 or 54 */ - uint16_t client_id = 0; - ts::protocol::PacketIdManager _packet_id_manager; - packet_buffers_t _packet_buffers; - std::array _packet_buffer_overflow{false}; + ecc_key identity{}; - std::array incoming_generation_estimators{}; /* implementation is thread save */ - uint8_t _packet_buffers_index = 0; + std::string initiv_command; + } crypto; + std::string generate_client_initiv(); - bool crypt_setupped{false}; - ts::connection::CryptHandler crypt_handler; - ts::connection::CompressionHandler compression_handler; - ts::connection::AcknowledgeManager acknowledge_handler; + uint16_t client_id = 0; + ts::protocol::PacketIdManager _packet_id_manager; + packet_buffers_t _packet_buffers; + std::array _packet_buffer_overflow{false}; - void handleCommandInitIVExpend(ts::Command&); - void handleCommandInitIVExpend2(ts::Command&); - void handleCommandInitServer(ts::Command&); + std::array incoming_generation_estimators{}; /* implementation is thread save */ + uint8_t _packet_buffers_index = 0; - struct { - std::chrono::system_clock::time_point ping_send_timestamp; - std::chrono::system_clock::time_point ping_received_timestamp; - std::chrono::microseconds value; - uint16_t ping_id; + bool crypt_setupped{false}; + ts::connection::CryptHandler crypt_handler; + ts::connection::CompressionHandler compression_handler; + ts::connection::AcknowledgeManager acknowledge_handler; - std::chrono::microseconds interval = std::chrono::microseconds(2500); - } ping; - void ping_send_request(); - }; - } + ConnectionStatistics statistics_{}; + + struct { + std::chrono::system_clock::time_point ping_send_timestamp{}; + std::chrono::system_clock::time_point ping_received_timestamp{}; + std::chrono::microseconds value{0}; + uint16_t ping_id{0}; + + std::chrono::microseconds interval{2500}; + } ping; + + void handleCommandInitIVExpend(ts::Command&); + void handleCommandInitIVExpend2(ts::Command&); + void handleCommandInitServer(ts::Command&); + + void ping_send_request(); + }; } \ No newline at end of file diff --git a/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp b/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp index 11f3e93..aa7ad19 100644 --- a/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp +++ b/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp @@ -16,14 +16,16 @@ using namespace ts::protocol; using namespace ts; inline void generate_random(uint8_t *destination, size_t length) { - while(length-- > 0) - *(destination++) = (uint8_t) rand(); + while(length-- > 0) { + *(destination++) = (uint8_t) rand(); + } } inline void write_reversed(uint8_t* destination, uint8_t* source, size_t length) { destination += length; - while(length-- > 0) - *(--destination) = *(source++); + while(length-- > 0) { + *(--destination) = *(source++); + } } inline bool solve_puzzle(mp_int& x, mp_int& n, mp_int& result, uint32_t level) { @@ -63,8 +65,10 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptrpow.state) - return; //TODO handle error? + if(packet_state != this->pow.state) { + return; //TODO handle error? + } + this->acknowledge_handler.reset(); /* we don't need an ack anymore for our init packet */ if(packet_state == pow_state::COOKIE_SET) { @@ -146,6 +150,7 @@ void ProtocolHandler::handlePacketInit(const std::shared_ptrpow.last_resend = system_clock::now(); this->send_packet(make_shared(PacketTypeInfo::Init1, PacketFlag::Unencrypted, this->pow.last_buffer)); } + mp_clear_multi(&point_x, &point_n, &result, nullptr); this->connection_state = connection_state::INIT_HIGH; } diff --git a/native/serverconnection/src/connection/ServerConnection.cpp b/native/serverconnection/src/connection/ServerConnection.cpp index 72131f9..a2cceca 100644 --- a/native/serverconnection/src/connection/ServerConnection.cpp +++ b/native/serverconnection/src/connection/ServerConnection.cpp @@ -65,6 +65,7 @@ NAN_MODULE_INIT(ServerConnection::Init) { Nan::SetPrototypeMethod(klass, "send_voice_data", ServerConnection::_send_voice_data); Nan::SetPrototypeMethod(klass, "send_voice_data_raw", ServerConnection::_send_voice_data_raw); Nan::SetPrototypeMethod(klass, "current_ping", ServerConnection::_current_ping); + Nan::SetPrototypeMethod(klass, "statistics", ServerConnection::statistics); constructor().Reset(Nan::GetFunction(klass).ToLocalChecked()); } @@ -620,7 +621,7 @@ void ServerConnection::send_voice_data(const void *buffer, size_t buffer_length, } void ServerConnection::close_connection() { - lock_guard lock(this->disconnect_lock); + lock_guard lock{this->disconnect_lock}; if(this->socket && this_thread::get_id() == this->socket->io_thread().get_id()) { logger::debug(category::connection, tr("close_connection() called in IO thread. Closing connection within event loop!")); if(!this->event_loop_execute_connection_close) { @@ -643,11 +644,13 @@ void ServerConnection::close_connection() { } void ServerConnection::execute_tick() { - if(this->protocol_handler) - this->protocol_handler->execute_tick(); + if(this->protocol_handler) { + this->protocol_handler->execute_tick(); + } - if(auto vc{this->voice_connection}; vc) - vc->execute_tick(); + if(auto vc{this->voice_connection}; vc) { + vc->execute_tick(); + } } void ServerConnection::_execute_callback_commands() { @@ -759,9 +762,30 @@ void ServerConnection::_execute_callback_disconnect(const std::string &reason) { NAN_METHOD(ServerConnection::_current_ping) { auto connection = ObjectWrap::Unwrap(info.Holder()); + lock_guard lock{connection->disconnect_lock}; + auto& phandler = connection->protocol_handler; if(phandler) info.GetReturnValue().Set((uint32_t) chrono::floor(phandler->current_ping()).count()); else info.GetReturnValue().Set(-1); +} + +NAN_METHOD(ServerConnection::statistics) { + auto connection = ObjectWrap::Unwrap(info.Holder()); + lock_guard lock{connection->disconnect_lock}; + + auto& phandler = connection->protocol_handler; + if(phandler) { + auto statistics = phandler->statistics(); + + auto result = Nan::New(); + Nan::Set(result, Nan::LocalString("voice_bytes_received"), Nan::New(statistics.voice_bytes_received)); + Nan::Set(result, Nan::LocalString("voice_bytes_send"), Nan::New(statistics.voice_bytes_send)); + Nan::Set(result, Nan::LocalString("control_bytes_received"), Nan::New(statistics.control_bytes_received)); + Nan::Set(result, Nan::LocalString("control_bytes_send"), Nan::New(statistics.control_bytes_send)); + info.GetReturnValue().Set(result); + } else { + info.GetReturnValue().Set(Nan::Undefined()); + } } \ No newline at end of file diff --git a/native/serverconnection/src/connection/ServerConnection.h b/native/serverconnection/src/connection/ServerConnection.h index cd15401..22eaf10 100644 --- a/native/serverconnection/src/connection/ServerConnection.h +++ b/native/serverconnection/src/connection/ServerConnection.h @@ -86,6 +86,7 @@ namespace tc { static NAN_METHOD(_send_voice_data_raw); static NAN_METHOD(_error_message); static NAN_METHOD(_current_ping); + static NAN_METHOD(statistics); std::unique_ptr callback_connect; std::unique_ptr callback_disconnect; diff --git a/package-lock.json b/package-lock.json index b20548e..31a1967 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "TeaClient", - "version": "1.4.13", + "version": "1.5.0-2", "lockfileVersion": 1, "requires": true, "dependencies": {