TorrentSession

class TorrentSession(val torrent: TorrentInfo, val disk: DiskIo, network: NetworkRuntime, scope: CoroutineScope, val peerId: Sha1Hash = generatePeerId(), val listenPort: Int = 6881, httpTracker: HttpTracker? = null, var maxPeers: Int = 50, tickIntervalMs: Long = 1000, settings: SettingsPack = SettingsPack(), utp: UtpSocketManager? = null, limiter: RateLimiter? = null, torrentBandwidth: TorrentBandwidth? = null, connections: ConnectionBudget? = null, resumeData: AddTorrentParams? = null, ipFilter: IpFilter? = null)

The download/upload engine for a single torrent. It is the live counterpart of libtorrent's torrent (torrent.cpp). It wires the pure-core pieces on coroutines: announce → collect peers → run a PeerConnection per peer → a PiecePicker-driven request pipeline → write blocks to DiskIo → verify completed pieces → broadcast have → and serve blocks back to peers we've unchoked.

Tunables come from the ported SettingsPack (the libtorrent defaults), exactly the knobs torrent/peer_connection read from m_settings: request_timeout, piece_timeout, request_queue_time, max_out_request_queue, initial_picker_threshold, strict_end_game_mode, unchoke_interval, optimistic_unchoke_interval, unchoke_slots_limit.

The request scheduler is a faithful port of request_blocks.cpp + the relevant peer_connection machinery:

  • Dynamic request queue (update_desired_queue_size): each peer's pipeline depth is request_queue_time × download-rate / block-size, clamped to [2, max_out_request_queue]. Slow start grows the depth by one per block until the rate stops climbing. Snubbed or end-game peers drop to 1.

  • True end-game (request_a_block): when the swarm has no free block left for a peer with an empty queue, the engine re-requests one block already asked of another peer (gated by strict_end_game_mode). The first copy to arrive is kept, and every other holder gets a cancel (torrent::cancel_block).

  • Snubbing (snub_peer): the engine snubs a peer that has outstanding requests but sends no payload for piece_timeout. A snubbed peer gets queue depth 1 and reverse picking, so it converges on common pieces instead of keeping rare pieces unavailable. The engine also cancels its newest request when that request blocks a piece from completing. A replacement is requested first, so the picker cannot hand the same block back.

  • Choking: a periodic Choker round (unchoke_interval) limits uploads to the fastest peers + one rotating optimistic slot. Choked peers are not served.

  • Rate limiting: payload moves through the engine's RateLimiter, which is the live io.github.yuroyami.kitetorrent.bandwidth.BandwidthManager wiring. Uploads acquire quota before the send and downloads after the receive, which applies back-pressure to the read loop.

  • µTP: with a UtpSocketManager attached, outgoing connections try uTP first and fall back to TCP (outgoing_utp + outgoing_tcp).

  • Verified resume, state machine, pause/resume, priorities and sequentialDownload as before.

A single Mutex guards all shared state (picker, have, the peer set); sends happen outside the lock. Every read and write of that state must hold the mutex.

Constructors

Link copied to clipboard
constructor(torrent: TorrentInfo, disk: DiskIo, network: NetworkRuntime, scope: CoroutineScope, peerId: Sha1Hash = generatePeerId(), listenPort: Int = 6881, httpTracker: HttpTracker? = null, maxPeers: Int = 50, tickIntervalMs: Long = 1000, settings: SettingsPack = SettingsPack(), utp: UtpSocketManager? = null, limiter: RateLimiter? = null, torrentBandwidth: TorrentBandwidth? = null, connections: ConnectionBudget? = null, resumeData: AddTorrentParams? = null, ipFilter: IpFilter? = null)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

The pieces we have verified on disk.

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
var onAlert: (Alert) -> Unit?

Alert sink (off-lock). The owning io.github.yuroyami.kitetorrent.session.engine.KiteTorrentEngine sets this to forward per-torrent events into the session alert queue. Defaults to null (no-op). Called at the event sites this engine touches: tracker reply/error, peer connect/disconnect/ban, piece finished, torrent finished, hash failures.

Link copied to clipboard
var onDhtPort: (host: String, port: Int) -> Unit?

Invoked (off-lock) with a DHT-capable peer's advertised UDP port whenever a BEP-5 port message arrives. The engine feeds it into the DHT as a fresh node. Null = ignore.

Link copied to clipboard

Invoked (off-lock) when the fast-resume state has gone stale and should be persisted: the torrent finished or was paused. The app responds by calling saveResumeData and writing the bytes (the port of libtorrent's save_resume_data / NEED_SAVE_RESUME).

Link copied to clipboard

Invoked (off-lock) whenever a piece is verified and completed.

Link copied to clipboard

Invoked (off-lock) whenever state changes.

Link copied to clipboard
Link copied to clipboard

Seed-time cap in seconds; 0 = unlimited. Pauses seeding once met.

Link copied to clipboard

Pick pieces in index order instead of rarest-first (torrent_flags::sequential_download).

Link copied to clipboard

Share-ratio cap in per-mille (2000 = 2.0×); 0 = unlimited. Pauses seeding once met.

Link copied to clipboard

Current lifecycle state.

Link copied to clipboard

Super-seeding / initial-seeding (torrent_flags::super_seeding). When seeding, advertise only one (rarest) piece to each peer at a time. Hand out the next one only once the peer announces it got the previous. The seed then pushes the whole torrent into the swarm with minimal redundant uploads while bootstrapping it.

Link copied to clipboard
Link copied to clipboard

Upload (unchoke) slots for this torrent: max_uploads. Settable live, default from settings.

Functions

Link copied to clipboard
suspend fun acceptInbound(conn: TcpConnection, remote: Handshake)

Inbound TCP peer: the engine's accept loop read the remote handshake off conn.

suspend fun acceptInbound(stream: ByteStream, close: suspend () -> Unit, remote: Handshake)

Inbound peer over any transport (TCP or µTP). stream must be positioned just past the remote's 68-byte handshake; close tears the transport down.

Link copied to clipboard

Apply fast-resume atp without hashing: whole pieces in AddTorrentParams.havePieces are flushed into the picker as had (piece_flushed, which is documented safe to call straight from open for exactly this), and each block recorded in AddTorrentParams.unfinishedPieces is marked finished so the picker won't re-request the partial-piece progress already on disk. Saved stats, the sequential flag and piece/file priorities are restored too. The port of torrent adopting resume data.

Link copied to clipboard

Connect to up to maxPeers of the given endpoints, each on its own coroutine.

Link copied to clipboard
suspend fun inEndgame(): Boolean

True while any peer is in end-game mode (double-requesting the last blocks).

Link copied to clipboard
suspend fun isSeeding(): Boolean
Link copied to clipboard
suspend fun numHave(): Int
Link copied to clipboard
suspend fun numPeers(): Int
Link copied to clipboard
suspend fun numSnubbedPeers(): Int

Peers currently snubbed (no payload despite outstanding requests).

Link copied to clipboard
suspend fun numUnchoked(): Int

Peers we're currently uploading to (unchoked), bounded by uploadSlots (max_uploads).

Link copied to clipboard
suspend fun pause()

Pause: stop requesting/serving and disconnect all peers. Idempotent. Sends event=stopped to the trackers and flushes the disk so partial-piece progress is durable.

Link copied to clipboard
suspend fun progress(): Float
Link copied to clipboard
suspend fun recheck(full: Boolean = false)

Verified resume: for each candidate piece, hash the bytes on disk and claim it only if the hash matches the torrent's piece hash. full = true rehashes every piece (use after a crash); otherwise it only checks pieces the disk layer reports present.

Link copied to clipboard
suspend fun resume()

Resume after a pause: re-announce and reconnect.

Link copied to clipboard
suspend fun saveResumeData(savePath: String = ""): AddTorrentParams

Capture the current piece state as fast-resume AddTorrentParams. This is the port of save_resume_data / write_resume_data. The result round-trips through ResumeData.write/read (embedding the raw info dict so the torrent reloads without its .torrent file) and feeds straight back into applyResumeData on the next run. Verified have pieces become both havePieces and verifiedPieces (every v1 piece is hash-checked before it's marked had); partially-downloaded pieces export their on-disk (FINISHED) blocks as an unfinished block bitmask.

Link copied to clipboard
suspend fun setFilePriority(fileIndex: Int, priority: Int)

Set a file's download priority. A piece shared by two files takes the higher of the two files' priorities (libtorrent's rule), recomputed across all files.

Link copied to clipboard
suspend fun setPiecePriority(piece: Int, priority: Int): Boolean

Set a single piece's priority (0 = don't download, 7 = highest).

Link copied to clipboard
suspend fun shutdown()

Graceful teardown: send event=stopped to the trackers, stop the tick, disconnect peers, then flush and close the disk layer. The suspend complement of stop for a clean shutdown (the port of torrent::abort + the disk_io::release_files/close path). Idempotent.

Link copied to clipboard
suspend fun start()

Adopt fast-resume state, or recheck when there's none, then start the maintenance tick, announce, and connect to peers. With resumeData present the saved have bitfield is trusted (no rehash). That is what fast resume is for: restarts skip the full-disk re-read recheck would otherwise do.

Link copied to clipboard
fun stop()

Stop the maintenance tick. The owning scope should also be cancelled to drop peers.