diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index bea3f81313..807ce4177c 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -58,6 +58,7 @@ use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundCloseFormPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; +use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; use pocketmine\network\mcpe\protocol\ModalFormRequestPacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket; @@ -109,6 +110,7 @@ use pocketmine\promise\PromiseResolver; use pocketmine\Server; use pocketmine\timings\Timings; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; use pocketmine\utils\BinaryStream; use pocketmine\utils\ObjectSet; @@ -194,6 +196,17 @@ class NetworkSession{ */ private ObjectSet $disposeHooks; + /** + * @var string[] + * @phpstan-var array + */ + private array $repeatedPacketFilters = []; + /** + * @var int[] + * @phpstan-var array + */ + private array $repeatedPacketFilterStats = []; + public function __construct( private Server $server, private NetworkSessionManager $manager, @@ -221,6 +234,8 @@ class NetworkSession{ $this->onSessionStartSuccess(...) )); + $this->addRepeatedPacketFilter(InventoryTransactionPacket::NETWORK_ID); + $this->manager->add($this); $this->logger->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_network_session_open())); } @@ -350,6 +365,44 @@ class NetworkSession{ } } + public function addRepeatedPacketFilter(int $packetId) : void{ + $this->repeatedPacketFilters[$packetId] = ""; + $this->repeatedPacketFilterStats[$packetId] = 0; + } + + public function removeRepeatedPacketFilter(int $packetId) : void{ + unset($this->repeatedPacketFilters[$packetId]); + unset($this->repeatedPacketFilterStats[$packetId]); + } + + /** + * Returns the stats for repeated packet filters, indexed by packet ID. + * The value is the number of times a packet was dropped due to being repeated. + * + * @return int[] + * @phpstan-return array + */ + public function getRepeatedPacketFilterStats() : array{ + return $this->repeatedPacketFilterStats; + } + + private function checkRepeatedPacketFilter(string $buffer) : bool{ + //TODO: would be great if we didn't repeat reading the ID inside PacketPool + $dummy = 0; + $packetId = Binary::readUnsignedVarInt($buffer, $dummy); + + if(isset($this->repeatedPacketFilters[$packetId])){ + if($buffer === $this->repeatedPacketFilters[$packetId]){ + $this->repeatedPacketFilterStats[$packetId]++; + return true; + } + + $this->repeatedPacketFilters[$packetId] = $buffer; + } + + return false; + } + /** * @throws PacketHandlingException */ @@ -403,6 +456,10 @@ class NetworkSession{ try{ $stream = new BinaryStream($decompressed); foreach(PacketBatch::decodeRaw($stream) as $buffer){ + if($this->checkRepeatedPacketFilter($buffer)){ + continue; + } + $this->gamePacketLimiter->decrement(); $packet = $this->packetPool->getPacket($buffer); if($packet === null){