mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
StandardPacketBroadcaster: Improve performance when broadcasting small packets
In refactors during PM4, I stripped out packet buffer caching, as it was problematic when events alter packets in undetectable ways. However, I never cleaned this part of the code up properly after enabling DataPacketSendEvent to include multiple packets and multiple targets, so we were still individually encoding the packet(s) for every single session if the sum total of the sizes was below 256 bytes. This change encodes packets once in the StandardPacketBroadcaster and retains their buffers to post to the session's send buffer directly if the resulting batch is below compression threshold. This code is still not optimal (see ##5589), but fixing this brings broadcasting performance back to PM3 levels, without any of PM3's problems.
This commit is contained in:
parent
75bb4f8da6
commit
6a64486f55
@ -177,7 +177,7 @@ class NetworkSession{
|
||||
|
||||
private ?EncryptionContext $cipher = null;
|
||||
|
||||
/** @var ClientboundPacket[] */
|
||||
/** @var string[] */
|
||||
private array $sendBuffer = [];
|
||||
|
||||
/**
|
||||
@ -264,23 +264,6 @@ class NetworkSession{
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClientboundPacket[] $packets
|
||||
*/
|
||||
public static function encodePacketBatchTimed(BinaryStream $stream, PacketSerializerContext $context, array $packets) : void{
|
||||
PacketBatch::encodeRaw($stream, array_map(function(ClientboundPacket $packet) use ($context) : string{
|
||||
$timings = Timings::getEncodeDataPacketTimings($packet);
|
||||
$timings->startTiming();
|
||||
try{
|
||||
$stream = PacketSerializer::encoder($context);
|
||||
$packet->encode($stream);
|
||||
return $stream->getBuffer();
|
||||
}finally{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
}, $packets));
|
||||
}
|
||||
|
||||
private function onPlayerCreated(Player $player) : void{
|
||||
if(!$this->isConnected()){
|
||||
//the remote player might have disconnected before spawn terrain generation was finished
|
||||
@ -498,7 +481,7 @@ class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->addToSendBuffer($packet);
|
||||
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $packet));
|
||||
if($immediate){
|
||||
$this->flushSendBuffer(true);
|
||||
}
|
||||
@ -512,16 +495,24 @@ class NetworkSession{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function addToSendBuffer(ClientboundPacket $packet) : void{
|
||||
$timings = Timings::getSendDataPacketTimings($packet);
|
||||
public static function encodePacketTimed(PacketSerializer $serializer, ClientboundPacket $packet) : string{
|
||||
$timings = Timings::getEncodeDataPacketTimings($packet);
|
||||
$timings->startTiming();
|
||||
try{
|
||||
$this->sendBuffer[] = $packet;
|
||||
$packet->encode($serializer);
|
||||
return $serializer->getBuffer();
|
||||
}finally{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function addToSendBuffer(string $buffer) : void{
|
||||
$this->sendBuffer[] = $buffer;
|
||||
}
|
||||
|
||||
private function flushSendBuffer(bool $immediate = false) : void{
|
||||
if(count($this->sendBuffer) > 0){
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
@ -534,7 +525,7 @@ class NetworkSession{
|
||||
}
|
||||
|
||||
$stream = new BinaryStream();
|
||||
self::encodePacketBatchTimed($stream, $this->packetSerializerContext, $this->sendBuffer);
|
||||
PacketBatch::encodeRaw($stream, $this->sendBuffer);
|
||||
|
||||
if($this->enableCompression){
|
||||
$promise = $this->server->prepareBatch(new PacketBatch($stream->getBuffer()), $this->compressor, $syncMode);
|
||||
|
@ -23,25 +23,35 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use function array_map;
|
||||
use function spl_object_id;
|
||||
|
||||
final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
public function __construct(private Server $server){}
|
||||
|
||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||
$buffers = [];
|
||||
$batchBuffers = [];
|
||||
|
||||
/** @var string[][] $packetBuffers */
|
||||
$packetBuffers = [];
|
||||
$compressors = [];
|
||||
/** @var NetworkSession[][][] $targetMap */
|
||||
$targetMap = [];
|
||||
foreach($recipients as $recipient){
|
||||
$serializerContext = $recipient->getPacketSerializerContext();
|
||||
$bufferId = spl_object_id($serializerContext);
|
||||
if(!isset($buffers[$bufferId])){
|
||||
if(!isset($batchBuffers[$bufferId])){
|
||||
$packetBuffers[$bufferId] = array_map(function(ClientboundPacket $packet) use ($serializerContext) : string{
|
||||
return NetworkSession::encodePacketTimed(PacketSerializer::encoder($serializerContext), $packet);
|
||||
}, $packets);
|
||||
$stream = new BinaryStream();
|
||||
NetworkSession::encodePacketBatchTimed($stream, $serializerContext, $packets);
|
||||
$buffers[$bufferId] = $stream->getBuffer();
|
||||
PacketBatch::encodeRaw($stream, $packetBuffers[$bufferId]);
|
||||
$batchBuffers[$bufferId] = $stream->getBuffer();
|
||||
}
|
||||
|
||||
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
|
||||
@ -52,17 +62,17 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
}
|
||||
|
||||
foreach($targetMap as $bufferId => $compressorMap){
|
||||
$buffer = $buffers[$bufferId];
|
||||
$batchBuffer = $batchBuffers[$bufferId];
|
||||
foreach($compressorMap as $compressorId => $compressorTargets){
|
||||
$compressor = $compressors[$compressorId];
|
||||
if(!$compressor->willCompress($buffer)){
|
||||
if(!$compressor->willCompress($batchBuffer)){
|
||||
foreach($compressorTargets as $target){
|
||||
foreach($packets as $pk){
|
||||
$target->addToSendBuffer($pk);
|
||||
foreach($packetBuffers[$bufferId] as $packetBuffer){
|
||||
$target->addToSendBuffer($packetBuffer);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$promise = $this->server->prepareBatch(new PacketBatch($buffer), $compressor);
|
||||
$promise = $this->server->prepareBatch(new PacketBatch($batchBuffer), $compressor);
|
||||
foreach($compressorTargets as $target){
|
||||
$target->queueCompressed($promise);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user