/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "NetStats.h" #include "network/NetServerSession.h" #include #include enum { Row_InData, Row_OutData, Row_LastSendTime, Row_LastRecvTime, Row_NextTimeout, Row_PacketsSent, Row_PacketsLost, Row_LastRTT, Row_RTT, Row_MTU, Row_ReliableInTransit, NumberRows }; CNetStatsTable::CNetStatsTable(ngtcp2_conn* conn): m_Connection{conn} { } CStr CNetStatsTable::GetName() { return "net"; } CStr CNetStatsTable::GetTitle() { if (m_Connection) return "Network client statistics"; else return "Network host statistics"; } size_t CNetStatsTable::GetNumberRows() { return NumberRows; } const std::vector& CNetStatsTable::GetColumns() { m_ColumnDescriptions.clear(); m_ColumnDescriptions.push_back(ProfileColumn("Name", 200)); if (m_Connection) m_ColumnDescriptions.push_back(ProfileColumn("Value", 80)); else { std::lock_guard lock(m_Mutex); for (size_t i = 0; i < m_LatchedData.size(); ++i) m_ColumnDescriptions.push_back(ProfileColumn("Peer "+CStr::FromUInt(i), 80)); } return m_ColumnDescriptions; } CStr CNetStatsTable::GetCellText(size_t row, size_t col) { // Return latched data, if we have any { std::lock_guard lock(m_Mutex); if (col > 0 && m_LatchedData.size() > col-1 && m_LatchedData[col-1].size() > row) return m_LatchedData[col-1][row]; } #define ROW(id, title, member) \ case id: \ if (col == 0) return title; \ if (m_Connection) return member; \ return "???" ngtcp2_conn_info info; if (col != 0) ngtcp2_conn_get_conn_info(m_Connection, &info); switch(row) { ROW(Row_InData, "incoming bytes", {}); ROW(Row_OutData, "outgoing bytes", {}); ROW(Row_LastSendTime, "last send time", {}); ROW(Row_LastRecvTime, "last receive time", {}); ROW(Row_NextTimeout, "next timeout", CStr::FromUInt(ngtcp2_conn_get_expiry(m_Connection))); ROW(Row_PacketsSent, "packets sent", {}); ROW(Row_PacketsLost, "packets lost", {}); ROW(Row_LastRTT, "last RTT", CStr::FromUInt(info.latest_rtt)); ROW(Row_RTT, "mean RTT", CStr::FromUInt(info.smoothed_rtt)); ROW(Row_MTU, "MTU", CStr::FromUInt(ngtcp2_conn_get_path_max_tx_udp_payload_size(m_Connection))); ROW(Row_ReliableInTransit, "reliable data in transit", CStr::FromUInt(info.bytes_in_flight)); default: return "???"; } #undef ROW } AbstractProfileTable* CNetStatsTable::GetChild(size_t /*row*/) { return 0; } void CNetStatsTable::LatchHostState(const std::span> sessions) { std::lock_guard lock(m_Mutex); #define ROW(id, title, member) \ m_LatchedData[i].push_back(CStr::FromUInt(info.member)); m_LatchedData.clear(); m_LatchedData.resize(sessions.size()); for (size_t i = 0; i < sessions.size(); ++i) { ngtcp2_conn_info info; ngtcp2_conn_get_conn_info(sessions[i]->m_Connection.m_QuicConnection.get(), &info); // ROW(Row_InData, "incoming bytes", bytes_recv); m_LatchedData[i].push_back({}); // ROW(Row_OutData, "outgoing bytes", bytes_sent); m_LatchedData[i].push_back({}); // ROW(Row_LastSendTime, "last send time", lastSendTime); m_LatchedData[i].push_back({}); // ROW(Row_LastRecvTime, "last receive time", lastReceiveTime); m_LatchedData[i].push_back({}); m_LatchedData[i].push_back(CStr::FromUInt(ngtcp2_conn_get_expiry( sessions[i]->m_Connection.m_QuicConnection.get()))); // ROW(Row_PacketsSent, "packets sent", pkt_sent); m_LatchedData[i].push_back({}); // ROW(Row_PacketsLost, "packets lost", pkt_lost); m_LatchedData[i].push_back({}); ROW(Row_LastRTT, "last RTT", latest_rtt); ROW(Row_RTT, "mean RTT", smoothed_rtt); m_LatchedData[i].push_back(CStr::FromUInt(ngtcp2_conn_get_path_max_tx_udp_payload_size( sessions[i]->m_Connection.m_QuicConnection.get()))); ROW(Row_ReliableInTransit, "reliable data in transit", bytes_in_flight); } #undef ROW }