mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
closes #5670 I'm not super happy with this fix, since it can still be broken if StandardPacketBroadcaster is replaced by something else. However, fixing that problem is probably going to require internal BC breaks, which are not suitable for a patch release.
103 lines
3.5 KiB
PHP
103 lines
3.5 KiB
PHP
<?php
|
|
|
|
/*
|
|
*
|
|
* ____ _ _ __ __ _ __ __ ____
|
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* @author PocketMine Team
|
|
* @link http://www.pocketmine.net/
|
|
*
|
|
*
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace pocketmine\network\mcpe;
|
|
|
|
use pocketmine\event\server\DataPacketSendEvent;
|
|
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
|
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
|
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
|
use pocketmine\Server;
|
|
use pocketmine\timings\Timings;
|
|
use pocketmine\utils\BinaryStream;
|
|
use function count;
|
|
use function log;
|
|
use function spl_object_id;
|
|
use function strlen;
|
|
|
|
final class StandardPacketBroadcaster implements PacketBroadcaster{
|
|
public function __construct(
|
|
private Server $server,
|
|
private PacketSerializerContext $protocolContext
|
|
){}
|
|
|
|
public function broadcastPackets(array $recipients, array $packets) : void{
|
|
//TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative
|
|
//implementation that doesn't fire events
|
|
$ev = new DataPacketSendEvent($recipients, $packets);
|
|
$ev->call();
|
|
if($ev->isCancelled()){
|
|
return;
|
|
}
|
|
$packets = $ev->getPackets();
|
|
|
|
$compressors = [];
|
|
|
|
/** @var NetworkSession[][] $targetsByCompressor */
|
|
$targetsByCompressor = [];
|
|
foreach($recipients as $recipient){
|
|
if($recipient->getPacketSerializerContext() !== $this->protocolContext){
|
|
throw new \InvalidArgumentException("Only recipients with the same protocol context as the broadcaster can be broadcast to by this broadcaster");
|
|
}
|
|
|
|
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
|
|
$compressor = $recipient->getCompressor();
|
|
$compressors[spl_object_id($compressor)] = $compressor;
|
|
|
|
$targetsByCompressor[spl_object_id($compressor)][] = $recipient;
|
|
}
|
|
|
|
$totalLength = 0;
|
|
$packetBuffers = [];
|
|
foreach($packets as $packet){
|
|
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet);
|
|
//varint length prefix + packet buffer
|
|
$totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer);
|
|
$packetBuffers[] = $buffer;
|
|
}
|
|
|
|
foreach($targetsByCompressor as $compressorId => $compressorTargets){
|
|
$compressor = $compressors[$compressorId];
|
|
|
|
$threshold = $compressor->getCompressionThreshold();
|
|
if(count($compressorTargets) > 1 && $threshold !== null && $totalLength >= $threshold){
|
|
//do not prepare shared batch unless we're sure it will be compressed
|
|
$stream = new BinaryStream();
|
|
PacketBatch::encodeRaw($stream, $packetBuffers);
|
|
$batchBuffer = $stream->getBuffer();
|
|
|
|
$promise = $this->server->prepareBatch(new PacketBatch($batchBuffer), $compressor, timings: Timings::$playerNetworkSendCompressBroadcast);
|
|
foreach($compressorTargets as $target){
|
|
$target->queueCompressed($promise);
|
|
}
|
|
}else{
|
|
foreach($compressorTargets as $target){
|
|
foreach($packetBuffers as $packetBuffer){
|
|
$target->addToSendBuffer($packetBuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|