Show any multiple connections from the same IP in peer list

The uniqueness of peers is now determined by their
IP, port and connection type (uTP etc.) instead of just their IP
This commit is contained in:
thalieht 2020-01-28 21:32:15 +02:00
parent c3ce1aaa3d
commit 5f415c292d
4 changed files with 59 additions and 21 deletions

View File

@ -68,3 +68,13 @@ QString PeerAddress::toString() const
: ip.toString();
return (ipStr + ':' + QString::number(port));
}
bool BitTorrent::operator==(const BitTorrent::PeerAddress &left, const BitTorrent::PeerAddress &right)
{
return (left.ip == right.ip) && (left.port == right.port);
}
uint BitTorrent::qHash(const BitTorrent::PeerAddress &addr, const uint seed)
{
return (::qHash(addr.ip, seed) ^ ::qHash(addr.port));
}

View File

@ -42,4 +42,7 @@ namespace BitTorrent
static PeerAddress parse(const QString &address);
QString toString() const;
};
bool operator==(const PeerAddress &left, const PeerAddress &right);
uint qHash(const PeerAddress &addr, uint seed);
}

View File

@ -57,6 +57,22 @@
#include "propertieswidget.h"
#include "uithememanager.h"
struct PeerEndpoint
{
BitTorrent::PeerAddress address;
QString connectionType; // matches return type of `PeerInfo::connectionType()`
};
bool operator==(const PeerEndpoint &left, const PeerEndpoint &right)
{
return (left.address == right.address) && (left.connectionType == right.connectionType);
}
uint qHash(const PeerEndpoint &peerEndpoint, const uint seed)
{
return (qHash(peerEndpoint.address, seed) ^ ::qHash(peerEndpoint.connectionType));
}
PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent)
, m_properties(parent)
@ -331,42 +347,45 @@ void PeerListWidget::loadPeers(const BitTorrent::TorrentHandle *torrent)
if (!torrent) return;
const QVector<BitTorrent::PeerInfo> peers = torrent->peers();
QSet<QString> existingPeers;
QSet<PeerEndpoint> existingPeers;
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
existingPeers << i.key();
for (const BitTorrent::PeerInfo &peer : peers) {
const BitTorrent::PeerAddress addr = peer.address();
if (addr.ip.isNull()) continue;
if (peer.address().ip.isNull()) continue;
const QString peerIp = addr.ip.toString();
bool isNewPeer = false;
updatePeer(peerIp, torrent, peer, isNewPeer);
if (!isNewPeer)
existingPeers.remove(peerIp);
updatePeer(torrent, peer, isNewPeer);
if (!isNewPeer) {
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
existingPeers.remove(peerEndpoint);
}
}
// Remove peers that are gone
for (const QString &ip : asConst(existingPeers)) {
const QStandardItem *item = m_peerItems.take(ip);
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers)) {
const QStandardItem *item = m_peerItems.take(peerEndpoint);
m_listModel->removeRow(item->row());
}
}
void PeerListWidget::updatePeer(const QString &ip, const BitTorrent::TorrentHandle *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer)
void PeerListWidget::updatePeer(const BitTorrent::TorrentHandle *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer)
{
auto itemIter = m_peerItems.find(ip);
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
const QString peerIp = peerEndpoint.address.ip.toString();
auto itemIter = m_peerItems.find(peerEndpoint);
isNewPeer = (itemIter == m_peerItems.end());
if (isNewPeer) {
// new item
const int row = m_listModel->rowCount();
m_listModel->insertRow(row);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), peerIp);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), peerIp, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), peerIp);
itemIter = m_peerItems.insert(ip, m_listModel->item(row, PeerListDelegate::IP));
itemIter = m_peerItems.insert(peerEndpoint, m_listModel->item(row, PeerListDelegate::IP));
}
const int row = (*itemIter)->row();
@ -386,7 +405,7 @@ void PeerListWidget::updatePeer(const QString &ip, const BitTorrent::TorrentHand
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWNLOADING_PIECE), downloadingFiles.join('\n'), Qt::ToolTipRole);
if (m_resolver)
m_resolver->resolve(ip);
m_resolver->resolve(peerIp);
if (m_resolveCountries) {
const QIcon icon = UIThemeManager::instance()->getFlagIcon(peer.country());
@ -400,9 +419,13 @@ void PeerListWidget::updatePeer(const QString &ip, const BitTorrent::TorrentHand
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname)
{
QStandardItem *item = m_peerItems.value(ip, nullptr);
if (item)
item->setData(hostname, Qt::DisplayRole);
for (auto iter = m_peerItems.cbegin(); iter != m_peerItems.cend(); ++iter) {
if (iter.key().address.ip.toString() == ip) {
QStandardItem *item {iter.value()};
item->setData(hostname, Qt::DisplayRole);
break;
}
}
}
void PeerListWidget::handleSortColumnChanged(const int col)

View File

@ -38,6 +38,8 @@ class QStandardItemModel;
class PeerListSortModel;
class PropertiesWidget;
struct PeerEndpoint;
namespace BitTorrent
{
class TorrentHandle;
@ -73,7 +75,7 @@ private slots:
void handleResolved(const QString &ip, const QString &hostname);
private:
void updatePeer(const QString &ip, const BitTorrent::TorrentHandle *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer);
void updatePeer(const BitTorrent::TorrentHandle *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer);
void wheelEvent(QWheelEvent *event) override;
@ -81,7 +83,7 @@ private:
PeerListSortModel *m_proxyModel = nullptr;
PropertiesWidget *m_properties = nullptr;
Net::ReverseResolution *m_resolver = nullptr;
QHash<QString, QStandardItem *> m_peerItems;
QHash<PeerEndpoint, QStandardItem *> m_peerItems;
bool m_resolveCountries;
};