diff --git a/include/engine.h b/include/engine.h index 29baa76..bf8bf04 100644 --- a/include/engine.h +++ b/include/engine.h @@ -139,6 +139,24 @@ uint32_t engine_loop_count(engine *e); void engine_torrent_status(engine *e, uint32_t torrent_id, torrent_status *out); +/* One connected peer's live state, for the UI's per-peer list. */ +typedef struct { + char ip[64]; + uint16_t port; + int32_t state; /* peer_state */ + int32_t unchoked; /* peer has unchoked us */ + uint64_t bytes_received; /* total payload bytes from this peer */ + double rate_bps; /* current download rate from this peer */ + uint32_t have_pieces; /* pieces this peer advertises */ + uint32_t num_pieces; /* torrent piece count (for progress ratio) */ +} engine_peer_info; + +/* Fill `out` with up to `max` connected peers (handshake completed) of a + * torrent; returns the number written. Best-effort snapshot read on the caller + * thread (like engine_torrent_status), so fine for status, not control. */ +uint32_t engine_peer_list(engine *e, uint32_t torrent_id, engine_peer_info *out, + uint32_t max); + /* Diagnostic: write a human-readable dump of one torrent's piece-selection and * per-connection state to `out`. Reports, for every still-wanted piece * (priority > 0), whether a peer has claimed it (requested), how many connected diff --git a/src/engine.c b/src/engine.c index 21f6216..7ac08d6 100644 --- a/src/engine.c +++ b/src/engine.c @@ -461,6 +461,35 @@ void engine_torrent_status(engine *e, uint32_t torrent_id, torrent_status *out) out->rtt_min_ms = rttmin; } +uint32_t engine_peer_list(engine *e, uint32_t torrent_id, engine_peer_info *out, + uint32_t max) { + if (!e || !out || max == 0) return 0; + torrent *t = find_locked(e, torrent_id); + if (!t) return 0; + loop *lp = t->lp; + uint32_t n = 0; + for (conn *c = lp->conns; c && n < max; c = c->next) { + if (c->tor != t) continue; + int st = atomic_load_explicit(&c->astate, memory_order_relaxed); + if (st != PEER_STATE_RUNNING && st != PEER_STATE_CHOKED) continue; + engine_peer_info *p = &out[n++]; + snprintf(p->ip, sizeof p->ip, "%s", c->ip); + p->port = c->port; + p->state = st; + p->unchoked = c->unchoked; + p->bytes_received = + atomic_load_explicit(&c->bytes_received, memory_order_relaxed); + p->rate_bps = c->rate_bps; + p->num_pieces = t->num_pieces; + uint32_t have = 0; + if (c->have_bits) + for (uint32_t i = 0; i < t->num_pieces; i++) + if (have_bit(c->have_bits, i)) have++; + p->have_pieces = have; + } + return n; +} + static const char *peer_state_name(int state) { switch (state) { case PEER_STATE_IDLE: return "idle";