Throttle resource pack sending using ack receipts

this isn't the best solution, as it limits the download speed somewhat, but it's relatively simple and works quite well.
closes #3127
This commit is contained in:
Dylan K. Taylor 2024-03-01 17:07:19 +00:00
parent 4abc36275c
commit b9a1ef1357
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -50,11 +50,22 @@ use function substr;
* packs to the client. * packs to the client.
*/ */
class ResourcePacksPacketHandler extends PacketHandler{ class ResourcePacksPacketHandler extends PacketHandler{
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB private const PACK_CHUNK_SIZE = 256 * 1024; //256KB
/**
* Larger values allow downloading more chunks at the same time, increasing download speed, but the client may choke
* and cause the download speed to drop (due to ACKs taking too long to arrive).
*/
private const MAX_CONCURRENT_CHUNK_REQUESTS = 1;
/** @var bool[][] uuid => [chunk index => hasSent] */ /** @var bool[][] uuid => [chunk index => hasSent] */
private array $downloadedChunks = []; private array $downloadedChunks = [];
/** @phpstan-var \SplQueue<array{ResourcePack, int}> */
private \SplQueue $requestQueue;
private int $activeRequests = 0;
/** /**
* @phpstan-param \Closure() : void $completionCallback * @phpstan-param \Closure() : void $completionCallback
*/ */
@ -62,7 +73,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
private NetworkSession $session, private NetworkSession $session,
private ResourcePackManager $resourcePackManager, private ResourcePackManager $resourcePackManager,
private \Closure $completionCallback private \Closure $completionCallback
){} ){
$this->requestQueue = new \SplQueue();
}
public function setUp() : void{ public function setUp() : void{
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{ $resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
@ -176,8 +189,38 @@ class ResourcePacksPacketHandler extends PacketHandler{
$this->downloadedChunks[$packId][$packet->chunkIndex] = true; $this->downloadedChunks[$packId][$packet->chunkIndex] = true;
} }
$this->session->sendDataPacket(ResourcePackChunkDataPacket::create($packId, $packet->chunkIndex, $offset, $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE))); $this->requestQueue->enqueue([$pack, $packet->chunkIndex]);
$this->processChunkRequestQueue();
return true; return true;
} }
private function processChunkRequestQueue() : void{
if($this->activeRequests >= self::MAX_CONCURRENT_CHUNK_REQUESTS || $this->requestQueue->isEmpty()){
return;
}
/**
* @var ResourcePack $pack
* @var int $chunkIndex
*/
[$pack, $chunkIndex] = $this->requestQueue->dequeue();
$packId = $pack->getPackId();
$offset = $chunkIndex * self::PACK_CHUNK_SIZE;
$chunkData = $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE);
$this->activeRequests++;
$this->session
->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData))
->onCompletion(
function() use ($packId, $chunkIndex) : void{
$this->activeRequests--;
$this->processChunkRequestQueue();
},
function() : void{
//this may have been rejected because of a disconnection - this will do nothing in that case
$this->disconnectWithError("Plugin interrupted sending of resource packs");
}
);
$this->session->getLogger()->debug("Sent resource pack $packId chunk $chunkIndex");
}
} }