diff --git a/src/block/MossCarpet.php b/src/block/MossCarpet.php index 1919531a672..400616ff364 100644 --- a/src/block/MossCarpet.php +++ b/src/block/MossCarpet.php @@ -34,9 +34,6 @@ public function isSolid() : bool{ return true; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)]; } diff --git a/src/network/mcpe/ChunkRequestTask.php b/src/network/mcpe/ChunkRequestTask.php index 81be59f74c2..9c3b9e6a65f 100644 --- a/src/network/mcpe/ChunkRequestTask.php +++ b/src/network/mcpe/ChunkRequestTask.php @@ -23,18 +23,21 @@ namespace pocketmine\network\mcpe; +use pocketmine\network\mcpe\compression\CompressBatchPromise; use pocketmine\network\mcpe\compression\Compressor; use pocketmine\network\mcpe\convert\TypeConverter; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; +use pocketmine\network\mcpe\protocol\LevelChunkPacket; +use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\network\mcpe\protocol\serializer\PacketBatch; +use pocketmine\network\mcpe\protocol\types\ChunkPosition; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\serializer\ChunkSerializer; use pocketmine\scheduler\AsyncTask; use pocketmine\thread\NonThreadSafeValue; -use pocketmine\utils\Binary; +use pocketmine\utils\BinaryStream; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; -/** @phpstan-ignore-next-line */ -use function xxhash64; +use function chr; class ChunkRequestTask extends AsyncTask{ private const TLS_KEY_PROMISE = "promise"; @@ -52,7 +55,7 @@ class ChunkRequestTask extends AsyncTask{ /** * @phpstan-param DimensionIds::* $dimensionId */ - public function __construct(int $chunkX, int $chunkZ, int $dimensionId, Chunk $chunk, TypeConverter $typeConverter, CachedChunkPromise $promise, Compressor $compressor){ + public function __construct(int $chunkX, int $chunkZ, int $dimensionId, Chunk $chunk, TypeConverter $typeConverter, CompressBatchPromise $promise, Compressor $compressor){ $this->compressor = new NonThreadSafeValue($compressor); $this->mappingProtocol = $typeConverter->getProtocolId(); @@ -67,42 +70,23 @@ public function __construct(int $chunkX, int $chunkZ, int $dimensionId, Chunk $c public function onRun() : void{ $chunk = FastChunkSerializer::deserializeTerrain($this->chunk); + $dimensionId = $this->dimensionId; - $cache = new CachedChunk(); - + $subCount = ChunkSerializer::getSubChunkCount($chunk, $dimensionId); $converter = TypeConverter::getInstance($this->mappingProtocol); - foreach(ChunkSerializer::serializeSubChunks($chunk, $this->dimensionId, $converter->getBlockTranslator(), $this->mappingProtocol) as $subChunk){ - /** @phpstan-ignore-next-line */ - $cache->addSubChunk(Binary::readLong(xxhash64($subChunk)), $subChunk); - } - - $encoder = PacketSerializer::encoder($this->mappingProtocol); - $biomeEncoder = clone $encoder; - ChunkSerializer::serializeBiomes($chunk, $this->dimensionId, $biomeEncoder); - /** @phpstan-ignore-next-line */ - $cache->setBiomes(Binary::readLong(xxhash64($chunkBuffer = $biomeEncoder->getBuffer())), $chunkBuffer); + $payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter, $this->tiles); - $chunkDataEncoder = clone $encoder; - ChunkSerializer::serializeChunkData($chunk, $chunkDataEncoder, $converter, $this->tiles); + $stream = new BinaryStream(); + PacketBatch::encodePackets($stream, $this->mappingProtocol, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]); - $cache->compressPackets( - $this->chunkX, - $this->chunkZ, - $this->dimensionId, - $chunkDataEncoder->getBuffer(), - $this->compressor->deserialize(), - $this->mappingProtocol - ); - - $this->setResult($cache); + $compressor = $this->compressor->deserialize(); + $protocolAddition = $this->mappingProtocol >= ProtocolInfo::PROTOCOL_1_20_60 ? chr($compressor->getNetworkId()) : ''; + $this->setResult($protocolAddition . $compressor->compress($stream->getBuffer())); } public function onCompletion() : void{ - /** @var CachedChunk $result */ - $result = $this->getResult(); - - /** @var CachedChunkPromise $promise */ + /** @var CompressBatchPromise $promise */ $promise = $this->fetchLocal(self::TLS_KEY_PROMISE); - $promise->resolve($result); + $promise->resolve($this->getResult()); } } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 503d847bffb..ec422775467 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -58,7 +58,6 @@ use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundCloseFormPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; -use pocketmine\network\mcpe\protocol\ClientCacheMissResponsePacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; use pocketmine\network\mcpe\protocol\ModalFormRequestPacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; @@ -86,7 +85,6 @@ use pocketmine\network\mcpe\protocol\types\AbilitiesData; use pocketmine\network\mcpe\protocol\types\AbilitiesLayer; use pocketmine\network\mcpe\protocol\types\BlockPosition; -use pocketmine\network\mcpe\protocol\types\ChunkCacheBlob; use pocketmine\network\mcpe\protocol\types\command\CommandData; use pocketmine\network\mcpe\protocol\types\command\CommandEnum; use pocketmine\network\mcpe\protocol\types\command\CommandOverload; @@ -120,10 +118,7 @@ use pocketmine\world\Position; use pocketmine\world\World; use pocketmine\YmlServerProperties; -use function array_keys; use function array_map; -use function array_replace; -use function array_values; use function base64_encode; use function bin2hex; use function count; diff --git a/src/network/mcpe/cache/ChunkCache.php b/src/network/mcpe/cache/ChunkCache.php index a19841417dd..796e2f5c265 100644 --- a/src/network/mcpe/cache/ChunkCache.php +++ b/src/network/mcpe/cache/ChunkCache.php @@ -36,6 +36,7 @@ use function count; use function is_string; use function spl_object_id; +use function strlen; /** * This class is used by the current MCPE protocol system to store cached chunk packets for fast resending. @@ -102,12 +103,7 @@ private function prepareChunkAsync(int $chunkX, int $chunkZ, int $chunkHash, Typ if($chunk === null){ throw new \InvalidArgumentException("Cannot request an unloaded chunk"); } - $protocolId = $typeConverter->getProtocolId(); - - if(isset($this->caches[$chunkHash][$protocolId])){ - ++$this->hits; - return $this->caches[$chunkHash][$protocolId]; - } + ++$this->misses; $this->world->timings->syncChunkSendPrepare->startTiming(); try{ @@ -124,7 +120,7 @@ private function prepareChunkAsync(int $chunkX, int $chunkZ, int $chunkHash, Typ $this->compressor ) ); - $this->caches[$chunkHash][$protocolId] = $promise; + $this->caches[$chunkHash][$protocolId = $typeConverter->getProtocolId()] = $promise; $promise->onResolve(function(CompressBatchPromise $promise) use ($chunkHash, $protocolId) : void{ //the promise may have been discarded or replaced if the chunk was unloaded or modified in the meantime if(($this->caches[$chunkHash][$protocolId] ?? null) === $promise){ diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 2ae707260c1..1768ff043bc 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -128,7 +128,7 @@ /** * This handler handles packets related to general gameplay. */ -class InGamePacketHandler extends ChunkRequestPacketHandler{ +class InGamePacketHandler extends PacketHandler{ private const MAX_FORM_RESPONSE_DEPTH = 2; //modal/simple will be 1, custom forms 2 - they will never contain anything other than string|int|float|bool|null protected float $lastRightClickTime = 0.0; @@ -145,11 +145,9 @@ class InGamePacketHandler extends ChunkRequestPacketHandler{ public function __construct( private Player $player, - NetworkSession $session, + private NetworkSession $session, private InventoryManager $inventoryManager - ){ - parent::__construct($session); - } + ){} public function handleText(TextPacket $packet) : bool{ if($packet->type === TextPacket::TYPE_CHAT){ diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index 1e8976fbe5b..cfa6427e3d2 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -49,19 +49,17 @@ /** * Handles the initial login phase of the session. This handler is used as the initial state. */ -class LoginPacketHandler extends ChunkRequestPacketHandler{ +class LoginPacketHandler extends PacketHandler{ /** * @phpstan-param \Closure(PlayerInfo) : void $playerInfoConsumer * @phpstan-param \Closure(bool $isAuthenticated, bool $authRequired, Translatable|string|null $error, ?string $clientPubKey) : void $authCallback */ public function __construct( private Server $server, - NetworkSession $session, + private NetworkSession $session, private \Closure $playerInfoConsumer, private \Closure $authCallback - ){ - parent::__construct($session); - } + ){} public function handleLogin(LoginPacket $packet) : bool{ $protocolVersion = $packet->protocol; diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index eeeaab69954..cfd7118a15f 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -49,15 +49,13 @@ /** * Handler used for the pre-spawn phase of the session. */ -class PreSpawnPacketHandler extends ChunkRequestPacketHandler{ +class PreSpawnPacketHandler extends PacketHandler{ public function __construct( private Server $server, private Player $player, - NetworkSession $session, + private NetworkSession $session, private InventoryManager $inventoryManager - ){ - parent::__construct($session); - } + ){} public function setUp() : void{ Timings::$playerNetworkSendPreSpawnGameData->startTiming(); diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index 435a21ac63b..13d64e3b974 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -51,7 +51,7 @@ * Handler used for the resource packs sequence phase of the session. This handler takes care of downloading resource * packs to the client. */ -class ResourcePacksPacketHandler extends ChunkRequestPacketHandler{ +class ResourcePacksPacketHandler extends PacketHandler{ private const PACK_CHUNK_SIZE = 256 * 1024; //256KB /** @@ -89,8 +89,6 @@ public function __construct( private bool $mustAccept, private \Closure $completionCallback ){ - parent::__construct($session); - $this->requestQueue = new \SplQueue(); foreach($resourcePackStack as $pack){ $this->resourcePacksById[$pack->getPackId()] = $pack; diff --git a/src/network/mcpe/handler/SpawnResponsePacketHandler.php b/src/network/mcpe/handler/SpawnResponsePacketHandler.php index 6c1de971f0e..2ebb9d2b6b1 100644 --- a/src/network/mcpe/handler/SpawnResponsePacketHandler.php +++ b/src/network/mcpe/handler/SpawnResponsePacketHandler.php @@ -23,18 +23,15 @@ namespace pocketmine\network\mcpe\handler; -use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket; -final class SpawnResponsePacketHandler extends ChunkRequestPacketHandler{ +final class SpawnResponsePacketHandler extends PacketHandler{ /** * @phpstan-param \Closure() : void $responseCallback */ - public function __construct(private \Closure $responseCallback, NetworkSession $session){ - parent::__construct($session); - } + public function __construct(private \Closure $responseCallback){} public function handleSetLocalPlayerAsInitialized(SetLocalPlayerAsInitializedPacket $packet) : bool{ ($this->responseCallback)(); diff --git a/src/network/mcpe/serializer/ChunkSerializer.php b/src/network/mcpe/serializer/ChunkSerializer.php index 55d327dde2c..87d9f4d6317 100644 --- a/src/network/mcpe/serializer/ChunkSerializer.php +++ b/src/network/mcpe/serializer/ChunkSerializer.php @@ -85,18 +85,18 @@ public static function getSubChunkCount(Chunk $chunk, int $dimensionId) : int{ * @phpstan-param DimensionIds::* $dimensionId * @return string[] */ - public static function serializeSubChunks(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, int $protocolId) : array + public static function serializeSubChunks(Chunk $chunk, int $dimensionId, TypeConverter $typeConverter) : array { - $stream = PacketSerializer::encoder($protocolId); + $stream = PacketSerializer::encoder($typeConverter->getProtocolId()); $subChunks = []; $subChunkCount = self::getSubChunkCount($chunk, $dimensionId); $writtenCount = 0; - [$minSubChunkIndex, $maxSubChunkIndex] = self::getDimensionChunkBounds($dimensionId); + [$minSubChunkIndex, ] = self::getDimensionChunkBounds($dimensionId); for($y = $minSubChunkIndex; $writtenCount < $subChunkCount; ++$y, ++$writtenCount){ $subChunkStream = clone $stream; - self::serializeSubChunk($chunk->getSubChunk($y), $blockTranslator, $subChunkStream, false); + self::serializeSubChunk($chunk->getSubChunk($y), $typeConverter->getBlockTranslator(), $subChunkStream, false); $subChunks[] = $subChunkStream->getBuffer(); } @@ -106,15 +106,15 @@ public static function serializeSubChunks(Chunk $chunk, int $dimensionId, BlockT /** * @phpstan-param DimensionIds::* $dimensionId */ - public static function serializeFullChunk(Chunk $chunk, int $dimensionId, TypeConverter $converter, int $protocolId, ?string $tiles = null) : string{ - $stream = PacketSerializer::encoder($protocolId); + public static function serializeFullChunk(Chunk $chunk, int $dimensionId, TypeConverter $typeConverter, ?string $tiles = null) : string{ + $stream = PacketSerializer::encoder($typeConverter->getProtocolId()); - foreach(self::serializeSubChunks($chunk, $dimensionId, $converter->getBlockTranslator(), $protocolId) as $subChunk){ + foreach(self::serializeSubChunks($chunk, $dimensionId, $typeConverter) as $subChunk){ $stream->put($subChunk); } self::serializeBiomes($chunk, $dimensionId, $stream); - self::serializeChunkData($chunk, $stream, $converter, $tiles); + self::serializeChunkData($chunk, $stream, $typeConverter, $tiles); return $stream->getBuffer(); } diff --git a/src/world/World.php b/src/world/World.php index fe02eab4163..0160b8fa04b 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -112,6 +112,7 @@ use pocketmine\world\particle\ProtocolParticle; use pocketmine\world\sound\BlockPlaceSound; use pocketmine\world\sound\BlockSound; +use pocketmine\world\sound\ProtocolSound; use pocketmine\world\sound\Sound; use pocketmine\world\utils\SubChunkExplorer; use pocketmine\YmlServerProperties; @@ -730,11 +731,19 @@ public function addSound(Vector3 $pos, Sound $sound, ?array $players = null) : v $players = $ev->getRecipients(); } - if($sound instanceof BlockSound){ - $closure = function(TypeConverter $typeConverter) use ($sound, $pos) : array{ - $sound->setBlockTranslator($typeConverter->getBlockTranslator()); - return $sound->encode($pos); - }; + if(($blockSound = ($sound instanceof BlockSound)) || $sound instanceof ProtocolSound){ + if($blockSound){ + $closure = function(TypeConverter $typeConverter) use ($sound, $pos) : array{ + $sound->setBlockTranslator($typeConverter->getBlockTranslator()); + return $sound->encode($pos); + }; + }else{ + /** @var ProtocolSound $sound */ + $closure = function(TypeConverter $typeConverter) use ($sound, $pos) : array{ + $sound->setProtocolId($typeConverter->getProtocolId()); + return $sound->encode($pos); + }; + } if($players === $this->getViewersForPosition($pos)){ $this->broadcastPacketToViewersByTypeConverter($pos, $closure); diff --git a/src/world/sound/NoteSound.php b/src/world/sound/NoteSound.php index 60825670920..25154a61cd7 100644 --- a/src/world/sound/NoteSound.php +++ b/src/world/sound/NoteSound.php @@ -26,9 +26,10 @@ use pocketmine\data\bedrock\NoteInstrumentIdMap; use pocketmine\math\Vector3; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; +use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\types\LevelSoundEvent; -class NoteSound implements Sound{ +class NoteSound extends ProtocolSound{ public function __construct( private NoteInstrument $instrument, private int $note @@ -40,6 +41,15 @@ public function __construct( public function encode(Vector3 $pos) : array{ $instrumentId = NoteInstrumentIdMap::getInstance()->toId($this->instrument); + + if($this->protocolId < ProtocolInfo::PROTOCOL_1_21_50){ + if($instrumentId === 5 || $instrumentId === 7){ + $instrumentId++; + }elseif($instrumentId === 6 || $instrumentId === 8){ + $instrumentId--; + } + } + return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::NOTE, $pos, false, ($instrumentId << 8) | $this->note)]; } } diff --git a/src/world/sound/ProtocolSound.php b/src/world/sound/ProtocolSound.php new file mode 100644 index 00000000000..b65a13837bf --- /dev/null +++ b/src/world/sound/ProtocolSound.php @@ -0,0 +1,33 @@ +protocolId = $protocolId; + } +} diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 9ab12576393..1fac0451a67 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -264,3 +264,38 @@ parameters: count: 1 path: ../rules/UnsafeForeachArrayOfStringRule.php + - + message: '#^Call to method pocketmine\\network\\mcpe\\convert\\TypeConverter\:\:__protocolConstruct\(\) on a separate line has no effect\.$#' + identifier: method.resultUnused + count: 1 + path: src/network/mcpe/convert/TypeConverter.php + + - + message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#2 \$output of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#3 \$output of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#5 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#6 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tools/generate-bedrock-data-from-packets.php