From ee44f4e691d54ed121a661e6468766555bfe3416 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Mon, 23 Sep 2024 16:21:01 +0200 Subject: [PATCH] netprof privapi + ui (wip histograms) --- src/CMakeLists.txt | 3 + src/main_screen.cpp | 7 +- src/main_screen.hpp | 2 + src/tox_netprof_ui.cpp | 310 +++++++++++++++++++++++++++++++++++++++ src/tox_netprof_ui.hpp | 54 +++++++ src/tox_private_impl.hpp | 18 +++ 6 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 src/tox_netprof_ui.cpp create mode 100644 src/tox_netprof_ui.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 882c67a..fea4477 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,6 +97,9 @@ target_sources(tomato PUBLIC ./tox_dht_cap_histo.hpp ./tox_dht_cap_histo.cpp + ./tox_netprof_ui.hpp + ./tox_netprof_ui.cpp + ./tox_friend_faux_offline_messaging.hpp ./tox_friend_faux_offline_messaging.cpp diff --git a/src/main_screen.cpp b/src/main_screen.cpp index 285f1a4..27d455c 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -41,7 +41,8 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme sw(conf), osui(os), tuiu(tc, conf), - tdch(tpi) + tdch(tpi), + tnui(tpi) { tel.subscribeAll(tc); @@ -260,6 +261,7 @@ Screen* MainScreen::render(float time_delta, bool&) { osui.render(); tuiu.render(); // render tdch.render(); // render + const float tnui_interval = tnui.render(time_delta); { // main window menubar injection if (ImGui::Begin("tomato")) { @@ -442,6 +444,7 @@ Screen* MainScreen::render(float time_delta, bool&) { if (!_window_hidden && _time_since_event < curr_profile.low_delay_window) { _render_interval = std::min(_render_interval, ctc_interval); _render_interval = std::min(_render_interval, msgtc_interval); + _render_interval = std::min(_render_interval, tnui_interval); _render_interval = std::clamp( _render_interval, @@ -452,6 +455,7 @@ Screen* MainScreen::render(float time_delta, bool&) { } else if (!_window_hidden && _time_since_event < curr_profile.mid_delay_window) { _render_interval = std::min(_render_interval, ctc_interval); _render_interval = std::min(_render_interval, msgtc_interval); + _render_interval = std::min(_render_interval, tnui_interval); _render_interval = std::clamp( _render_interval, @@ -485,6 +489,7 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { const float pm_interval = pm.tick(time_delta); // compute tdch.tick(time_delta); // compute + tnui.tick(time_delta); // compute mts.iterate(); // compute (after mfs) diff --git a/src/main_screen.hpp b/src/main_screen.hpp index 1aa5939..3d8f495 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -32,6 +32,7 @@ #include "./object_store_ui.hpp" #include "./tox_ui_utils.hpp" #include "./tox_dht_cap_histo.hpp" +#include "./tox_netprof_ui.hpp" #include "./tox_friend_faux_offline_messaging.hpp" #if TOMATO_TOX_AV @@ -88,6 +89,7 @@ struct MainScreen final : public Screen { ObjectStoreUI osui; ToxUIUtils tuiu; ToxDHTCapHisto tdch; + ToxNetprofUI tnui; PluginManager pm; // last, so it gets destroyed first diff --git a/src/tox_netprof_ui.cpp b/src/tox_netprof_ui.cpp new file mode 100644 index 0000000..4107772 --- /dev/null +++ b/src/tox_netprof_ui.cpp @@ -0,0 +1,310 @@ +#include "./tox_netprof_ui.hpp" + +#include + +static const char* typedPkgIDToString(Tox_Netprof_Packet_Type type, uint8_t id) { + // pain + if (type == TOX_NETPROF_PACKET_TYPE_UDP) { + switch (id) { + case TOX_NETPROF_PACKET_ID_ZERO: return "Ping request"; + case TOX_NETPROF_PACKET_ID_ONE: return "Ping response"; + case TOX_NETPROF_PACKET_ID_TWO: return "Get nodes request"; + case TOX_NETPROF_PACKET_ID_FOUR: return "Send nodes response"; + case TOX_NETPROF_PACKET_ID_COOKIE_REQUEST: return "Cookie request"; + case TOX_NETPROF_PACKET_ID_COOKIE_RESPONSE: return "Cookie response"; + case TOX_NETPROF_PACKET_ID_CRYPTO_HS: return "Crypto handshake"; + case TOX_NETPROF_PACKET_ID_CRYPTO_DATA: return "Crypto data"; + case TOX_NETPROF_PACKET_ID_CRYPTO: return "Encrypted data"; + case TOX_NETPROF_PACKET_ID_LAN_DISCOVERY: return "LAN discovery"; + case TOX_NETPROF_PACKET_ID_GC_HANDSHAKE: return "DHT groupchat handshake"; + case TOX_NETPROF_PACKET_ID_GC_LOSSLESS: return "DHT groupchat lossless"; + case TOX_NETPROF_PACKET_ID_GC_LOSSY: return "DHT groupchat lossy"; + case TOX_NETPROF_PACKET_ID_ONION_SEND_INITIAL: return "Onion send init"; + case TOX_NETPROF_PACKET_ID_ONION_SEND_1: return "Onion send 1"; + case TOX_NETPROF_PACKET_ID_ONION_SEND_2: return "Onion send 2"; + case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST_OLD: return "DHT announce request (old)"; + case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE_OLD: return "DHT announce response (old)"; + case TOX_NETPROF_PACKET_ID_ONION_DATA_REQUEST: return "Onion data request"; + case TOX_NETPROF_PACKET_ID_ONION_DATA_RESPONSE: return "Onion data response"; + case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST: return "DHT announce request"; + case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE: return "DHT announce response"; + case TOX_NETPROF_PACKET_ID_ONION_RECV_3: return "Onion receive 3"; + case TOX_NETPROF_PACKET_ID_ONION_RECV_2: return "Onion receive 2"; + case TOX_NETPROF_PACKET_ID_ONION_RECV_1: return "Onion receive 1"; + case TOX_NETPROF_PACKET_ID_FORWARD_REQUEST: return "DHT forwarding request"; + case TOX_NETPROF_PACKET_ID_FORWARDING: return "DHT forwarding"; + case TOX_NETPROF_PACKET_ID_FORWARD_REPLY: return "DHT forward reply"; + case TOX_NETPROF_PACKET_ID_DATA_SEARCH_REQUEST: return "DHT data search request"; + case TOX_NETPROF_PACKET_ID_DATA_SEARCH_RESPONSE: return "DHT data search response"; + case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_REQUEST: return "DHT data retrieve request"; + case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_RESPONSE: return "DHT data retrieve response"; + case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_REQUEST: return "DHT store announce request"; + case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_RESPONSE: return "DHT store announce response"; + case TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO: return "Bootstrap info"; + } + } else if (type == TOX_NETPROF_PACKET_TYPE_TCP) { // TODO: or client/server + switch (id) { + case TOX_NETPROF_PACKET_ID_ZERO: return "Routing request"; + case TOX_NETPROF_PACKET_ID_ONE: return "Routing response"; + case TOX_NETPROF_PACKET_ID_TWO: return "Connection notification"; + case TOX_NETPROF_PACKET_ID_TCP_DISCONNECT: return "disconnect notification"; + case TOX_NETPROF_PACKET_ID_FOUR: return "Ping packet"; + case TOX_NETPROF_PACKET_ID_TCP_PONG: return "pong packet"; + case TOX_NETPROF_PACKET_ID_TCP_OOB_SEND: return "out-of-band send"; + case TOX_NETPROF_PACKET_ID_TCP_OOB_RECV: return "out-of-band receive"; + case TOX_NETPROF_PACKET_ID_TCP_ONION_REQUEST: return "onion request"; + case TOX_NETPROF_PACKET_ID_TCP_ONION_RESPONSE: return "onion response"; + case TOX_NETPROF_PACKET_ID_TCP_DATA: return "data"; + //case TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO: return "Bootstrap info"; + } + } + + return "UNK"; +} + +void ToxNetprofUI::tick(float time_delta) { + if (!_enabled) { + return; + } + + _time_since_last_add += time_delta; + if (_time_since_last_add >= _value_add_interval) { + _time_since_last_add = 0.f; // very loose + + if (_udp_tctx.empty()) { + _udp_tctx.push_back(0.f); + _udp_tctx_prev = _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT); + } else { + const auto new_value = _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT); + _udp_tctx.push_back(new_value - _udp_tctx_prev); + _udp_tctx_prev = new_value; + } + + if (_udp_tcrx.empty()) { + _udp_tcrx.push_back(0.f); + _udp_tcrx_prev = _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV); + } else { + const auto new_value = _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV); + _udp_tcrx.push_back(new_value - _udp_tcrx_prev); + _udp_tcrx_prev = new_value; + } + + if (_udp_tbtx.empty()) { + _udp_tbtx.push_back(0.f); + _udp_tbtx_prev = _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT); + } else { + const auto new_value = _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT); + _udp_tbtx.push_back(new_value - _udp_tbtx_prev); + _udp_tbtx_prev = new_value; + } + + if (_udp_tbrx.empty()) { + _udp_tbrx.push_back(0.f); + _udp_tbrx_prev = _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV); + } else { + const auto new_value = _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV); + _udp_tbrx.push_back(new_value - _udp_tbrx_prev); + _udp_tbrx_prev = new_value; + } + + if (_udp_tbrx.empty()) { + } + + // TODO: limit + while (_udp_tctx.size() > 5*60) { + _udp_tctx.erase(_udp_tctx.begin()); + } + + while (_udp_tcrx.size() > 5*60) { + _udp_tcrx.erase(_udp_tcrx.begin()); + } + + while (_udp_tbtx.size() > 5*60) { + _udp_tbtx.erase(_udp_tbtx.begin()); + } + + while (_udp_tbrx.size() > 5*60) { + _udp_tbrx.erase(_udp_tbrx.begin()); + } + } +} + +float ToxNetprofUI::render(float time_delta) { + { // main window menubar injection + // assumes the window "tomato" was rendered already by cg + if (ImGui::Begin("tomato")) { + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Tox")) { + ImGui::SeparatorText("Net diagnostics"); + + if (ImGui::MenuItem("Breakdown table", nullptr, _show_window_table)) { + _show_window_table = !_show_window_table; + } + + ImGui::Checkbox("histogram logging", &_enabled); + + if (ImGui::MenuItem("Netprof histograms", nullptr, _show_window_histo)) { + _show_window_histo = !_show_window_histo; + } + + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + } + ImGui::End(); + } + + if (_show_window_table) { + if (ImGui::Begin("Tox Netprof table", &_show_window_table)) { + ImGui::Text("UDP total Count tx/rx: %zu/%zu", + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV) + ); + ImGui::Text("UDP total Bytes tx/rx: %zu/%zu", + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_UDP, TOX_NETPROF_DIRECTION_RECV) + ); + ImGui::Text("TCP total Count tx/rx: %zu/%zu (client: %zu/%zu; server: %zu/%zu)", + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP, TOX_NETPROF_DIRECTION_RECV), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP_CLIENT, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP_CLIENT, TOX_NETPROF_DIRECTION_RECV), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP_SERVER, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalCount(TOX_NETPROF_PACKET_TYPE_TCP_SERVER, TOX_NETPROF_DIRECTION_RECV) + ); + ImGui::Text("TCP total Bytes tx/rx: %zu/%zu (client: %zu/%zu; server: %zu/%zu)", + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP, TOX_NETPROF_DIRECTION_RECV), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP_CLIENT, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP_CLIENT, TOX_NETPROF_DIRECTION_RECV), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP_SERVER, TOX_NETPROF_DIRECTION_SENT), + _tpi.toxNetprofGetPacketTotalBytes(TOX_NETPROF_PACKET_TYPE_TCP_SERVER, TOX_NETPROF_DIRECTION_RECV) + ); + + // TODO: color types (tcp/udp and maybe dht) + + static float decay_rate = 3.f; + ImGui::SliderFloat("heat decay (/s)", &decay_rate, 0.f, 50.0f); + + // type (udp/tcp), id/name, count tx, count rx, bytes tx, bytes rx + if (ImGui::BeginTable("per packet", 6, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + + ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible + ImGui::TableSetupColumn("type"); + ImGui::TableSetupColumn("pkt type"); + ImGui::TableSetupColumn("count tx"); + ImGui::TableSetupColumn("count rx"); + ImGui::TableSetupColumn("bytes tx"); + ImGui::TableSetupColumn("bytes rx"); + + ImGui::TableHeadersRow(); + + auto value_fn = [time_delta](size_t i, uint64_t value, auto& prev_map, auto& heat_map, const float scale = 0.2f) { + ImGui::TableNextColumn(); + ImGui::Text("%zu", value); + if (prev_map.count(i)) { + const auto delta = value - prev_map[i]; + float& heat = heat_map[i]; + heat += delta * scale; // count 0.1, bytes 0.02? + + // TODO: actual color function + float green = 0.7f; + if (heat > 1.f) { + green -= (heat - 1.f) * 0.1f; + } + + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.9f, green, 0.0f, heat))); + + //ImGui::SameLine(); + //ImGui::Text("%.2f", heat); + + //heat *= 0.94f; + float decay = decay_rate * time_delta; + if (decay > 0.999f) { + decay = 0.999f; + } + heat *= (1.f - decay); + } + prev_map[i] = value; + }; + + for (size_t i = 0; i < 0xff; i++) { + if (i == 0x10) { + continue; + } + const auto count_sent = _tpi.toxNetprofGetPacketIdCount(TOX_NETPROF_PACKET_TYPE_UDP, i, TOX_NETPROF_DIRECTION_SENT); + const auto count_received = _tpi.toxNetprofGetPacketIdCount(TOX_NETPROF_PACKET_TYPE_UDP, i, TOX_NETPROF_DIRECTION_RECV); + + if (count_sent == 0 && count_received == 0) { + continue; // skip empty + } + + ImGui::TableNextColumn(); + ImGui::TextUnformatted("UDP"); + + ImGui::TableNextColumn(); + ImGui::Text("%02zx(%s)", i, typedPkgIDToString(TOX_NETPROF_PACKET_TYPE_UDP, i)); + + value_fn(i, count_sent, _udp_ctx_prev, _udp_ctx_heat); + + value_fn(i, count_received, _udp_crx_prev, _udp_crx_heat); + + value_fn(i, _tpi.toxNetprofGetPacketIdBytes(TOX_NETPROF_PACKET_TYPE_UDP, i, TOX_NETPROF_DIRECTION_SENT), _udp_btx_prev, _udp_btx_heat, 0.005f); + + value_fn(i, _tpi.toxNetprofGetPacketIdBytes(TOX_NETPROF_PACKET_TYPE_UDP, i, TOX_NETPROF_DIRECTION_RECV), _udp_brx_prev, _udp_brx_heat, 0.005f); + } + + for (size_t i = 0; i <= 0x10; i++) { + const auto count_sent = _tpi.toxNetprofGetPacketIdCount(TOX_NETPROF_PACKET_TYPE_TCP, i, TOX_NETPROF_DIRECTION_SENT); + const auto count_received = _tpi.toxNetprofGetPacketIdCount(TOX_NETPROF_PACKET_TYPE_TCP, i, TOX_NETPROF_DIRECTION_RECV); + + if (count_sent == 0 && count_received == 0) { + continue; // skip empty + } + + ImGui::TableNextColumn(); + ImGui::TextUnformatted("TCP"); + + ImGui::TableNextColumn(); + ImGui::Text("%02zx(%s)", i, typedPkgIDToString(TOX_NETPROF_PACKET_TYPE_TCP, i)); + + value_fn(i, count_sent, _tcp_ctx_prev, _tcp_ctx_heat); + + value_fn(i, count_received, _tcp_crx_prev, _tcp_crx_heat); + + value_fn(i, _tpi.toxNetprofGetPacketIdBytes(TOX_NETPROF_PACKET_TYPE_TCP, i, TOX_NETPROF_DIRECTION_SENT), _tcp_btx_prev, _tcp_btx_heat, 0.005f); + + value_fn(i, _tpi.toxNetprofGetPacketIdBytes(TOX_NETPROF_PACKET_TYPE_TCP, i, TOX_NETPROF_DIRECTION_RECV), _tcp_brx_prev, _tcp_brx_heat, 0.005f); + } + + ImGui::EndTable(); + } + } + ImGui::End(); + } + + if (_show_window_histo) { + if (ImGui::Begin("Tox Netprof histograms", &_show_window_histo)) { + if (_enabled) { + const float line_height = ImGui::GetTextLineHeight(); + ImGui::PlotHistogram("udp total count sent##histograms", _udp_tctx.data(), _udp_tctx.size(), 0, nullptr, 0.f, FLT_MAX, {0, 3*line_height}); + ImGui::PlotHistogram("udp total count received##histograms", _udp_tcrx.data(), _udp_tcrx.size(), 0, nullptr, 0.f, FLT_MAX, {0, 3*line_height}); + ImGui::PlotHistogram("udp total bytes sent##histograms", _udp_tbtx.data(), _udp_tbtx.size(), 0, nullptr, 0.f, FLT_MAX, {0, 3*line_height}); + ImGui::PlotHistogram("udp total bytes received##histograms", _udp_tbrx.data(), _udp_tbrx.size(), 0, nullptr, 0.f, FLT_MAX, {0, 3*line_height}); + } else { + ImGui::TextUnformatted("logging disabled!"); + if (ImGui::Button("enable")) { + _enabled = true; + } + } + } + ImGui::End(); + } + + if (_show_window_table) { + return 0.1f; // min 10fps + } + return 2.f; +} + diff --git a/src/tox_netprof_ui.hpp b/src/tox_netprof_ui.hpp new file mode 100644 index 0000000..15d8ce5 --- /dev/null +++ b/src/tox_netprof_ui.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "./tox_private_impl.hpp" + +#include +#include +#include + +class ToxNetprofUI { + ToxPrivateImpl& _tpi; + + bool _enabled {true}; + bool _show_window_table {false}; + bool _show_window_histo {false}; + + // table delta + std::map _udp_ctx_prev; + std::map _udp_crx_prev; + std::map _udp_btx_prev; + std::map _udp_brx_prev; + std::map _tcp_ctx_prev; + std::map _tcp_crx_prev; + std::map _tcp_btx_prev; + std::map _tcp_brx_prev; + + // table heat + std::map _udp_ctx_heat; + std::map _udp_crx_heat; + std::map _udp_btx_heat; + std::map _udp_brx_heat; + std::map _tcp_ctx_heat; + std::map _tcp_crx_heat; + std::map _tcp_btx_heat; + std::map _tcp_brx_heat; + + // histogram totals + uint64_t _udp_tctx_prev; + uint64_t _udp_tcrx_prev; + uint64_t _udp_tbtx_prev; + uint64_t _udp_tbrx_prev; + std::vector _udp_tctx; + std::vector _udp_tcrx; + std::vector _udp_tbtx; + std::vector _udp_tbrx; + + const float _value_add_interval {1.f}; // every second + float _time_since_last_add {0.f}; + + public: + ToxNetprofUI(ToxPrivateImpl& tpi) : _tpi(tpi) {} + + void tick(float time_delta); + float render(float time_delta); +}; diff --git a/src/tox_private_impl.hpp b/src/tox_private_impl.hpp index 706d8ef..adbe085 100644 --- a/src/tox_private_impl.hpp +++ b/src/tox_private_impl.hpp @@ -32,4 +32,22 @@ struct ToxPrivateImpl : public ToxPrivateI { return {std::nullopt, err}; } } + + // TODO: add to interface + uint64_t toxNetprofGetPacketIdCount(Tox_Netprof_Packet_Type type, uint8_t id, Tox_Netprof_Direction direction) { + return tox_netprof_get_packet_id_count(_tox, type, id, direction); + } + + uint64_t toxNetprofGetPacketTotalCount(Tox_Netprof_Packet_Type type, Tox_Netprof_Direction direction) { + return tox_netprof_get_packet_total_count(_tox, type, direction); + } + + uint64_t toxNetprofGetPacketIdBytes(Tox_Netprof_Packet_Type type, uint8_t id, Tox_Netprof_Direction direction) { + return tox_netprof_get_packet_id_bytes(_tox, type, id, direction); + } + + uint64_t toxNetprofGetPacketTotalBytes(Tox_Netprof_Packet_Type type, Tox_Netprof_Direction direction) { + return tox_netprof_get_packet_total_bytes(_tox, type, direction); + } + };