diff --git a/.github/workflows/update-updater-api.yml b/.github/workflows/update-updater-api.yml index dc40d17e9..906278ef3 100644 --- a/.github/workflows/update-updater-api.yml +++ b/.github/workflows/update-updater-api.yml @@ -25,13 +25,25 @@ jobs: - name: Download new release information run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json - - name: Detect channel + - name: Detect channels id: channel - run: echo CHANNEL=$(jq -r '.channel' new_build_info.json) >> $GITHUB_OUTPUT - - - name: Copy release information run: | - cp new_build_info.json channels/${{ steps.channel.outputs.CHANNEL }}.json + CHANNEL=$(jq -r '.channel' new_build_info.json) + VERSION=${{ steps.tag-name.outputs.TAG_NAME }} + echo CHANNEL=$CHANNEL >> $GITHUB_OUTPUT + if [ "$CHANNEL" == "stable" ]; then + echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT + echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT + else + echo MAJOR=$(echo $VERSION | cut -d. -f1)-$CHANNEL >> $GITHUB_OUTPUT + echo MINOR=$(echo $VERSION | cut -d. -f1-2)-$CHANNEL >> $GITHUB_OUTPUT + fi + + - name: Update channel info + run: | + cp new_build_info.json "channels/${{ steps.channel.outputs.CHANNEL }}.json" + cp new_build_info.json "channels/${{ steps.channel.outputs.MAJOR }}.json" + cp new_build_info.json "channels/${{ steps.channel.outputs.MINOR }}.json" rm new_build_info.json - name: Commit changes diff --git a/build/make-release.php b/build/make-release.php index 4466e7b6f..7a570eb35 100644 --- a/build/make-release.php +++ b/build/make-release.php @@ -38,7 +38,6 @@ use function is_string; use function max; use function preg_match; use function preg_replace; -use function sleep; use function sprintf; use function str_pad; use function strlen; @@ -160,9 +159,6 @@ function main() : void{ replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel); systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit"); systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit"); - echo "pushing changes in 5 seconds\n"; - sleep(5); - systemWrapper('git push origin HEAD ' . $currentVer->getBaseVersion(), "failed to push changes to remote"); } main(); diff --git a/build/php b/build/php index 71b9f9d2d..a464454d1 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 71b9f9d2d7b3ee45d9475803b96c080f5f22f373 +Subproject commit a464454d1ed946baabd32a02ddcf66361374b99c diff --git a/src/Server.php b/src/Server.php index 6ded86c44..d2f4f4b79 100644 --- a/src/Server.php +++ b/src/Server.php @@ -43,7 +43,6 @@ use pocketmine\event\player\PlayerCreationEvent; use pocketmine\event\player\PlayerDataSaveEvent; use pocketmine\event\player\PlayerLoginEvent; use pocketmine\event\server\CommandEvent; -use pocketmine\event\server\DataPacketSendEvent; use pocketmine\event\server\QueryRegenerateEvent; use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\Language; @@ -54,13 +53,19 @@ use pocketmine\network\mcpe\compression\CompressBatchPromise; use pocketmine\network\mcpe\compression\CompressBatchTask; use pocketmine\network\mcpe\compression\Compressor; use pocketmine\network\mcpe\compression\ZlibCompressor; +use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary; use pocketmine\network\mcpe\encryption\EncryptionContext; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\PacketBroadcaster; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; +use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\raklib\RakLibInterface; +use pocketmine\network\mcpe\StandardEntityEventBroadcaster; +use pocketmine\network\mcpe\StandardPacketBroadcaster; use pocketmine\network\Network; use pocketmine\network\NetworkInterfaceStartException; use pocketmine\network\query\DedicatedQueryNetworkInterface; @@ -1165,10 +1170,18 @@ class Server{ return !$anyWorldFailedToLoad; } - private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{ + private function startupPrepareConnectableNetworkInterfaces( + string $ip, + int $port, + bool $ipV6, + bool $useQuery, + PacketBroadcaster $packetBroadcaster, + EntityEventBroadcaster $entityEventBroadcaster, + PacketSerializerContext $packetSerializerContext + ) : bool{ $prettyIp = $ipV6 ? "[$ip]" : $ip; try{ - $rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6)); + $rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext)); }catch(NetworkInterfaceStartException $e){ $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed( $ip, @@ -1194,11 +1207,15 @@ class Server{ private function startupPrepareNetworkInterfaces() : bool{ $useQuery = $this->configGroup->getConfigBool("enable-query", true); + $packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()); + $packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext); + $entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster); + if( - !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery) || + !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext) || ( $this->configGroup->getConfigBool("enable-ipv6", true) && - !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery) + !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext) ) ){ return false; @@ -1330,47 +1347,10 @@ class Server{ /** * @param Player[] $players * @param ClientboundPacket[] $packets + * @deprecated */ public function broadcastPackets(array $players, array $packets) : bool{ - if(count($packets) === 0){ - throw new \InvalidArgumentException("Cannot broadcast empty list of packets"); - } - - return Timings::$broadcastPackets->time(function() use ($players, $packets) : bool{ - /** @var NetworkSession[] $recipients */ - $recipients = []; - foreach($players as $player){ - if($player->isConnected()){ - $recipients[] = $player->getNetworkSession(); - } - } - if(count($recipients) === 0){ - return false; - } - - $ev = new DataPacketSendEvent($recipients, $packets); - $ev->call(); - if($ev->isCancelled()){ - return false; - } - $recipients = $ev->getTargets(); - $packets = $ev->getPackets(); - - /** @var PacketBroadcaster[] $broadcasters */ - $broadcasters = []; - /** @var NetworkSession[][] $broadcasterTargets */ - $broadcasterTargets = []; - foreach($recipients as $recipient){ - $broadcaster = $recipient->getBroadcaster(); - $broadcasters[spl_object_id($broadcaster)] = $broadcaster; - $broadcasterTargets[spl_object_id($broadcaster)][] = $recipient; - } - foreach($broadcasters as $broadcaster){ - $broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets); - } - - return true; - }); + return NetworkBroadcastUtils::broadcastPackets($players, $packets); } /** diff --git a/src/entity/Entity.php b/src/entity/Entity.php index b4cc56cfc..a9bede352 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -44,6 +44,8 @@ use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\AddActorPacket; use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket; use pocketmine\network\mcpe\protocol\SetActorMotionPacket; @@ -756,7 +758,7 @@ abstract class Entity{ $this->spawnTo($player); } }else{ - $this->server->broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create( + NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create( $this->id, $this->getOffsetPosition($this->location), $this->location->pitch, @@ -771,7 +773,7 @@ abstract class Entity{ } protected function broadcastMotion() : void{ - $this->server->broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]); + NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]); } public function getGravity() : float{ @@ -1508,7 +1510,7 @@ abstract class Entity{ $id = spl_object_id($player); if(isset($this->hasSpawned[$id])){ if($send){ - $player->getNetworkSession()->onEntityRemoved($this); + $player->getNetworkSession()->getEntityEventBroadcaster()->onEntityRemoved([$player->getNetworkSession()], $this); } unset($this->hasSpawned[$id]); } @@ -1519,9 +1521,11 @@ abstract class Entity{ * player moves, viewers will once again be able to see the entity. */ public function despawnFromAll() : void{ - foreach($this->hasSpawned as $player){ - $this->despawnFrom($player); - } + NetworkBroadcastUtils::broadcastEntityEvent( + $this->hasSpawned, + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEntityRemoved($recipients, $this) + ); + $this->hasSpawned = []; } /** @@ -1595,9 +1599,7 @@ abstract class Entity{ $targets = $targets ?? $this->hasSpawned; $data = $data ?? $this->getAllNetworkData(); - foreach($targets as $p){ - $p->getNetworkSession()->syncActorData($this, $data); - } + NetworkBroadcastUtils::broadcastEntityEvent($targets, fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->syncActorData($recipients, $this, $data)); } /** @@ -1651,7 +1653,7 @@ abstract class Entity{ * @param Player[]|null $targets */ public function broadcastAnimation(Animation $animation, ?array $targets = null) : void{ - $this->server->broadcastPackets($targets ?? $this->getViewers(), $animation->encode()); + NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $animation->encode()); } /** @@ -1660,7 +1662,7 @@ abstract class Entity{ */ public function broadcastSound(Sound $sound, ?array $targets = null) : void{ if(!$this->silent){ - $this->server->broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location)); + NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location)); } } diff --git a/src/entity/Human.php b/src/entity/Human.php index 24448347d..a8b6f9511 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -48,6 +48,8 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\convert\SkinAdapterSingleton; use pocketmine\network\mcpe\convert\TypeConverter; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; @@ -165,7 +167,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ * @param Player[]|null $targets */ public function sendSkin(?array $targets = null) : void{ - $this->server->broadcastPackets($targets ?? $this->hasSpawned, [ + NetworkBroadcastUtils::broadcastPackets($targets ?? $this->hasSpawned, [ PlayerSkinPacket::create($this->getUniqueId(), "", "", SkinAdapterSingleton::get()->toSkinData($this->skin)) ]); } @@ -180,9 +182,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } public function emote(string $emoteId) : void{ - foreach($this->getViewers() as $player){ - $player->getNetworkSession()->onEmote($this, $emoteId); - } + NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEmote($recipients, $this, $emoteId) + ); } public function getHungerManager() : HungerManager{ @@ -258,11 +261,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->xpManager = new ExperienceManager($this); $this->inventory = new PlayerInventory($this); - $syncHeldItem = function() : void{ - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onMobMainHandItemChange($this); - } - }; + $syncHeldItem = fn() => NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this) + ); $this->inventory->getListeners()->add(new CallbackInventoryListener( function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{ if($slot === $this->inventory->getHeldItemIndex()){ @@ -303,11 +305,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if($offHand !== null){ $this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand)); } - $this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(function() : void{ - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onMobOffHandItemChange($this); - } - })); + $this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobOffHandItemChange($recipients, $this) + ))); $enderChestInventoryTag = $nbt->getListTag(self::TAG_ENDER_CHEST_INVENTORY); if($enderChestInventoryTag !== null){ @@ -321,11 +322,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } $this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0)); - $this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{ - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onMobMainHandItemChange($this); - } - }); + $this->inventory->getHeldItemIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this) + )); $this->hungerManager->setFood((float) $nbt->getInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood())); $this->hungerManager->setExhaustion($nbt->getFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion())); @@ -474,11 +474,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } protected function sendSpawnPacket(Player $player) : void{ + $networkSession = $player->getNetworkSession(); if(!($this instanceof Player)){ - $player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))])); + $networkSession->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))])); } - $player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create( + $networkSession->sendDataPacket(AddPlayerPacket::create( $this->getUniqueId(), $this->getName(), $this->getId(), @@ -508,11 +509,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ //TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately $this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]); - $player->getNetworkSession()->onMobArmorChange($this); - $player->getNetworkSession()->onMobOffHandItemChange($this); + $entityEventBroadcaster = $networkSession->getEntityEventBroadcaster(); + $entityEventBroadcaster->onMobArmorChange([$networkSession], $this); + $entityEventBroadcaster->onMobOffHandItemChange([$networkSession], $this); if(!($this instanceof Player)){ - $player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)])); + $networkSession->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)])); } } diff --git a/src/entity/Living.php b/src/entity/Living.php index 09e609b84..b30ab7f87 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -50,6 +50,8 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; @@ -127,13 +129,10 @@ abstract class Living extends Entity{ $this->armorInventory = new ArmorInventory($this); //TODO: load/save armor inventory contents - $this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange( - function(Inventory $unused) : void{ - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onMobArmorChange($this); - } - } - )); + $this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this) + ))); $health = $this->getMaxHealth(); @@ -831,7 +830,8 @@ abstract class Living extends Entity{ protected function sendSpawnPacket(Player $player) : void{ parent::sendSpawnPacket($player); - $player->getNetworkSession()->onMobArmorChange($this); + $networkSession = $player->getNetworkSession(); + $networkSession->getEntityEventBroadcaster()->onMobArmorChange([$networkSession], $this); } protected function syncNetworkData(EntityMetadataCollection $properties) : void{ diff --git a/src/entity/object/ItemEntity.php b/src/entity/object/ItemEntity.php index 4a9eef0d4..90756525e 100644 --- a/src/entity/object/ItemEntity.php +++ b/src/entity/object/ItemEntity.php @@ -35,6 +35,8 @@ use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\convert\TypeConverter; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\AddItemActorPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; @@ -321,9 +323,10 @@ class ItemEntity extends Entity{ return; } - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onPlayerPickUpItem($player, $this); - } + NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this) + ); $inventory = $ev->getInventory(); if($inventory !== null){ diff --git a/src/entity/projectile/Arrow.php b/src/entity/projectile/Arrow.php index e41e4ab28..5a288ba72 100644 --- a/src/entity/projectile/Arrow.php +++ b/src/entity/projectile/Arrow.php @@ -33,6 +33,8 @@ use pocketmine\event\entity\ProjectileHitEvent; use pocketmine\item\VanillaItems; use pocketmine\math\RayTraceResult; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\network\mcpe\EntityEventBroadcaster; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; @@ -188,9 +190,10 @@ class Arrow extends Projectile{ return; } - foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->onPlayerPickUpItem($player, $this); - } + NetworkBroadcastUtils::broadcastEntityEvent( + $this->getViewers(), + fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this) + ); $ev->getInventory()?->addItem($ev->getItem()); $this->flagForDespawn(); diff --git a/src/event/server/DataPacketPreReceiveEvent.php b/src/event/server/DataPacketPreReceiveEvent.php new file mode 100644 index 000000000..8369fa257 --- /dev/null +++ b/src/event/server/DataPacketPreReceiveEvent.php @@ -0,0 +1,54 @@ +origin; + } + + public function getPacketId() : int{ + return $this->packetId; + } + + public function getPacketBuffer() : string{ + return $this->packetBuffer; + } +} diff --git a/src/network/mcpe/EntityEventBroadcaster.php b/src/network/mcpe/EntityEventBroadcaster.php new file mode 100644 index 000000000..252563149 --- /dev/null +++ b/src/network/mcpe/EntityEventBroadcaster.php @@ -0,0 +1,93 @@ + $properties + */ + public function syncActorData(array $recipients, Entity $entity, array $properties) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onEntityEffectAdded(array $recipients, Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onEntityRemoved(array $recipients, Entity $entity) : void; + + /** + * TODO: expand this to more than just humans + * + * @param NetworkSession[] $recipients + */ + public function onMobMainHandItemChange(array $recipients,Human $mob) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onMobOffHandItemChange(array $recipients, Human $mob) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onMobArmorChange(array $recipients, Living $mob) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onPickUpItem(array $recipients, Entity $collector, Entity $pickedUp) : void; + + /** + * @param NetworkSession[] $recipients + */ + public function onEmote(array $recipients, Human $from, string $emoteId) : void; +} diff --git a/src/network/mcpe/NetworkBroadcastUtils.php b/src/network/mcpe/NetworkBroadcastUtils.php new file mode 100644 index 000000000..81d5411ee --- /dev/null +++ b/src/network/mcpe/NetworkBroadcastUtils.php @@ -0,0 +1,104 @@ +time(function() use ($recipients, $packets) : bool{ + /** @var NetworkSession[] $sessions */ + $sessions = []; + foreach($recipients as $player){ + if($player->isConnected()){ + $sessions[] = $player->getNetworkSession(); + } + } + if(count($sessions) === 0){ + return false; + } + + $ev = new DataPacketSendEvent($sessions, $packets); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $sessions = $ev->getTargets(); + $packets = $ev->getPackets(); + + /** @var PacketBroadcaster[] $uniqueBroadcasters */ + $uniqueBroadcasters = []; + /** @var NetworkSession[][] $broadcasterTargets */ + $broadcasterTargets = []; + foreach($sessions as $recipient){ + $broadcaster = $recipient->getBroadcaster(); + $uniqueBroadcasters[spl_object_id($broadcaster)] = $broadcaster; + $broadcasterTargets[spl_object_id($broadcaster)][spl_object_id($recipient)] = $recipient; + } + foreach($uniqueBroadcasters as $broadcaster){ + $broadcaster->broadcastPackets($broadcasterTargets[spl_object_id($broadcaster)], $packets); + } + + return true; + }); + } + + /** + * @param Player[] $recipients + * @phpstan-param \Closure(EntityEventBroadcaster, array) : void $callback + */ + public static function broadcastEntityEvent(array $recipients, \Closure $callback) : void{ + $uniqueBroadcasters = []; + $broadcasterTargets = []; + + foreach($recipients as $recipient){ + $session = $recipient->getNetworkSession(); + $broadcaster = $session->getEntityEventBroadcaster(); + $uniqueBroadcasters[spl_object_id($broadcaster)] = $broadcaster; + $broadcasterTargets[spl_object_id($broadcaster)][spl_object_id($session)] = $session; + } + + foreach($uniqueBroadcasters as $k => $broadcaster){ + $callback($broadcaster, $broadcasterTargets[$k]); + } + } +} diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 938f76398..60c69f2b3 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -23,13 +23,9 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use pocketmine\data\bedrock\EffectIdMap; -use pocketmine\entity\Attribute; use pocketmine\entity\effect\EffectInstance; -use pocketmine\entity\Entity; -use pocketmine\entity\Human; -use pocketmine\entity\Living; use pocketmine\event\player\PlayerDuplicateLoginEvent; +use pocketmine\event\server\DataPacketPreReceiveEvent; use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketSendEvent; use pocketmine\form\Form; @@ -60,10 +56,6 @@ use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; -use pocketmine\network\mcpe\protocol\EmotePacket; -use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; -use pocketmine\network\mcpe\protocol\MobEffectPacket; -use pocketmine\network\mcpe\protocol\MobEquipmentPacket; use pocketmine\network\mcpe\protocol\ModalFormRequestPacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket; @@ -73,19 +65,16 @@ use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayStatusPacket; use pocketmine\network\mcpe\protocol\ProtocolInfo; -use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket; -use pocketmine\network\mcpe\protocol\SetActorDataPacket; use pocketmine\network\mcpe\protocol\SetDifficultyPacket; use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket; use pocketmine\network\mcpe\protocol\SetTimePacket; use pocketmine\network\mcpe\protocol\SetTitlePacket; -use pocketmine\network\mcpe\protocol\TakeItemActorPacket; use pocketmine\network\mcpe\protocol\TextPacket; use pocketmine\network\mcpe\protocol\ToastRequestPacket; use pocketmine\network\mcpe\protocol\TransferPacket; @@ -97,16 +86,10 @@ use pocketmine\network\mcpe\protocol\types\command\CommandEnum; use pocketmine\network\mcpe\protocol\types\command\CommandParameter; use pocketmine\network\mcpe\protocol\types\command\CommandPermissions; use pocketmine\network\mcpe\protocol\types\DimensionIds; -use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute; -use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; -use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData; -use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\network\mcpe\protocol\UpdateAdventureSettingsPacket; -use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use pocketmine\network\NetworkSessionManager; use pocketmine\network\PacketHandlingException; use pocketmine\permission\DefaultPermissionNames; @@ -134,7 +117,6 @@ use function hrtime; use function in_array; use function intdiv; use function json_encode; -use function ksort; use function min; use function random_bytes; use function strcasecmp; @@ -144,7 +126,6 @@ use function substr; use function time; use function ucfirst; use const JSON_THROW_ON_ERROR; -use const SORT_NUMERIC; class NetworkSession{ private const INCOMING_PACKET_BATCH_PER_TICK = 2; //usually max 1 per tick, but transactions may arrive separately @@ -203,6 +184,7 @@ class NetworkSession{ private PacketSerializerContext $packetSerializerContext, private PacketSender $sender, private PacketBroadcaster $broadcaster, + private EntityEventBroadcaster $entityEventBroadcaster, private Compressor $compressor, private string $ip, private int $port @@ -275,10 +257,10 @@ class NetworkSession{ $effectManager = $this->player->getEffects(); $effectManager->getEffectAddHooks()->add($effectAddHook = function(EffectInstance $effect, bool $replacesOldEffect) : void{ - $this->onEntityEffectAdded($this->player, $effect, $replacesOldEffect); + $this->entityEventBroadcaster->onEntityEffectAdded([$this], $this->player, $effect, $replacesOldEffect); }); $effectManager->getEffectRemoveHooks()->add($effectRemoveHook = function(EffectInstance $effect) : void{ - $this->onEntityEffectRemoved($this->player, $effect); + $this->entityEventBroadcaster->onEntityEffectRemoved([$this], $this->player, $effect); }); $this->disposeHooks->add(static function() use ($effectManager, $effectAddHook, $effectRemoveHook) : void{ $effectManager->getEffectAddHooks()->remove($effectAddHook); @@ -432,6 +414,12 @@ class NetworkSession{ $timings->startTiming(); try{ + $ev = new DataPacketPreReceiveEvent($this, $packet->pid(), $buffer); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $decodeTimings = Timings::getDecodeDataPacketTimings($packet); $decodeTimings->startTiming(); try{ @@ -449,18 +437,18 @@ class NetworkSession{ $decodeTimings->stopTiming(); } - $handlerTimings = Timings::getHandleDataPacketTimings($packet); - $handlerTimings->startTiming(); - try{ - //TODO: I'm not sure DataPacketReceiveEvent should be included in the handler timings, but it needs to be - //included for now to ensure the receivePacket timings are counted the way they were before - $ev = new DataPacketReceiveEvent($this, $packet); - $ev->call(); - if(!$ev->isCancelled() && ($this->handler === null || !$packet->handle($this->handler))){ - $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + $ev = new DataPacketReceiveEvent($this, $packet); + $ev->call(); + if(!$ev->isCancelled()){ + $handlerTimings = Timings::getHandleDataPacketTimings($packet); + $handlerTimings->startTiming(); + try{ + if($this->handler === null || !$packet->handle($this->handler)){ + $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + } + }finally{ + $handlerTimings->stopTiming(); } - }finally{ - $handlerTimings->stopTiming(); } }finally{ $timings->stopTiming(); @@ -552,6 +540,8 @@ class NetworkSession{ public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; } + public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; } + public function getCompressor() : Compressor{ return $this->compressor; } @@ -849,7 +839,7 @@ class NetworkSession{ } public function onServerRespawn() : void{ - $this->syncAttributes($this->player, $this->player->getAttributeMap()->getAll()); + $this->entityEventBroadcaster->syncAttributes([$this], $this->player, $this->player->getAttributeMap()->getAll()); $this->player->sendData(null); $this->syncAbilities($this->player); @@ -958,41 +948,6 @@ class NetworkSession{ )); } - /** - * @param Attribute[] $attributes - */ - public function syncAttributes(Living $entity, array $attributes) : void{ - if(count($attributes) > 0){ - $this->sendDataPacket(UpdateAttributesPacket::create($entity->getId(), array_map(function(Attribute $attr) : NetworkAttribute{ - return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []); - }, $attributes), 0)); - } - } - - /** - * @param MetadataProperty[] $properties - * @phpstan-param array $properties - */ - public function syncActorData(Entity $entity, array $properties) : void{ - //TODO: HACK! as of 1.18.10, the client responds differently to the same data ordered in different orders - for - //example, sending HEIGHT in the list before FLAGS when unsetting the SWIMMING flag results in a hitbox glitch - ksort($properties, SORT_NUMERIC); - $this->sendDataPacket(SetActorDataPacket::create($entity->getId(), $properties, new PropertySyncData([], []), 0)); - } - - public function onEntityEffectAdded(Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void{ - //TODO: we may need yet another effect <=> ID map in the future depending on protocol changes - $this->sendDataPacket(MobEffectPacket::add($entity->getId(), $replacesOldEffect, EffectIdMap::getInstance()->toId($effect->getType()), $effect->getAmplifier(), $effect->isVisible(), $effect->getDuration())); - } - - public function onEntityEffectRemoved(Living $entity, EffectInstance $effect) : void{ - $this->sendDataPacket(MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType()))); - } - - public function onEntityRemoved(Entity $entity) : void{ - $this->sendDataPacket(RemoveActorPacket::create($entity->getId())); - } - public function syncAvailableCommands() : void{ $commandData = []; foreach($this->server->getCommandMap()->getCommands() as $name => $command){ @@ -1141,36 +1096,6 @@ class NetworkSession{ return $this->invManager; } - /** - * TODO: expand this to more than just humans - */ - public function onMobMainHandItemChange(Human $mob) : void{ - //TODO: we could send zero for slot here because remote players don't need to know which slot was selected - $inv = $mob->getInventory(); - $this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), $inv->getHeldItemIndex(), ContainerIds::INVENTORY)); - } - - public function onMobOffHandItemChange(Human $mob) : void{ - $inv = $mob->getOffHandInventory(); - $this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, 0, ContainerIds::OFFHAND)); - } - - public function onMobArmorChange(Living $mob) : void{ - $inv = $mob->getArmorInventory(); - $converter = TypeConverter::getInstance(); - $this->sendDataPacket(MobArmorEquipmentPacket::create( - $mob->getId(), - ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())), - ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())), - ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())), - ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())) - )); - } - - public function onPlayerPickUpItem(Player $collector, Entity $pickedUp) : void{ - $this->sendDataPacket(TakeItemActorPacket::create($collector->getId(), $pickedUp->getId())); - } - /** * @param Player[] $players */ @@ -1214,10 +1139,6 @@ class NetworkSession{ $this->sendDataPacket(SetTitlePacket::setAnimationTimes($fadeIn, $stay, $fadeOut)); } - public function onEmote(Human $from, string $emoteId) : void{ - $this->sendDataPacket(EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER)); - } - public function onToastNotification(string $title, string $body) : void{ $this->sendDataPacket(ToastRequestPacket::create($title, $body)); } @@ -1257,7 +1178,7 @@ class NetworkSession{ $this->player->doChunkRequests(); $dirtyAttributes = $this->player->getAttributeMap()->needSend(); - $this->syncAttributes($this->player, $dirtyAttributes); + $this->entityEventBroadcaster->syncAttributes([$this], $this->player, $dirtyAttributes); foreach($dirtyAttributes as $attribute){ //TODO: we might need to send these to other players in the future //if that happens, this will need to become more complex than a flag on the attribute itself diff --git a/src/network/mcpe/StandardEntityEventBroadcaster.php b/src/network/mcpe/StandardEntityEventBroadcaster.php new file mode 100644 index 000000000..da0fac6ef --- /dev/null +++ b/src/network/mcpe/StandardEntityEventBroadcaster.php @@ -0,0 +1,143 @@ +broadcaster->broadcastPackets($recipients, [$packet]); + } + + public function syncAttributes(array $recipients, Living $entity, array $attributes) : void{ + if(count($attributes) > 0){ + $this->sendDataPacket($recipients, UpdateAttributesPacket::create( + $entity->getId(), + array_map(fn(Attribute $attr) => new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []), $attributes), + 0 + )); + } + } + + public function syncActorData(array $recipients, Entity $entity, array $properties) : void{ + //TODO: HACK! as of 1.18.10, the client responds differently to the same data ordered in different orders - for + //example, sending HEIGHT in the list before FLAGS when unsetting the SWIMMING flag results in a hitbox glitch + ksort($properties, SORT_NUMERIC); + $this->sendDataPacket($recipients, SetActorDataPacket::create($entity->getId(), $properties, new PropertySyncData([], []), 0)); + } + + public function onEntityEffectAdded(array $recipients, Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void{ + //TODO: we may need yet another effect <=> ID map in the future depending on protocol changes + $this->sendDataPacket($recipients, MobEffectPacket::add( + $entity->getId(), + $replacesOldEffect, + EffectIdMap::getInstance()->toId($effect->getType()), + $effect->getAmplifier(), + $effect->isVisible(), + $effect->getDuration() + )); + } + + public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void{ + $this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType()))); + } + + public function onEntityRemoved(array $recipients, Entity $entity) : void{ + $this->sendDataPacket($recipients, RemoveActorPacket::create($entity->getId())); + } + + public function onMobMainHandItemChange(array $recipients, Human $mob) : void{ + //TODO: we could send zero for slot here because remote players don't need to know which slot was selected + $inv = $mob->getInventory(); + $this->sendDataPacket($recipients, MobEquipmentPacket::create( + $mob->getId(), + ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), + $inv->getHeldItemIndex(), + $inv->getHeldItemIndex(), + ContainerIds::INVENTORY + )); + } + + public function onMobOffHandItemChange(array $recipients, Human $mob) : void{ + $inv = $mob->getOffHandInventory(); + $this->sendDataPacket($recipients, MobEquipmentPacket::create( + $mob->getId(), + ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), + 0, + 0, + ContainerIds::OFFHAND + )); + } + + public function onMobArmorChange(array $recipients, Living $mob) : void{ + $inv = $mob->getArmorInventory(); + $converter = TypeConverter::getInstance(); + $this->sendDataPacket($recipients, MobArmorEquipmentPacket::create( + $mob->getId(), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())) + )); + } + + public function onPickUpItem(array $recipients, Entity $collector, Entity $pickedUp) : void{ + $this->sendDataPacket($recipients, TakeItemActorPacket::create($collector->getId(), $pickedUp->getId())); + } + + public function onEmote(array $recipients, Human $from, string $emoteId) : void{ + $this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER)); + } +} diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index f83cff64d..1ed0f16ae 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -30,6 +30,7 @@ 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; @@ -60,7 +61,8 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ $packetBuffers = []; foreach($packets as $packet){ $buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet); - $totalLength += strlen($buffer); + //varint length prefix + packet buffer + $totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer); $packetBuffers[] = $buffer; } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 04c51d3fb..3eeecbb86 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -114,7 +114,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs()); $this->session->getLogger()->debug("Sending attributes"); - $this->session->syncAttributes($this->player, $this->player->getAttributeMap()->getAll()); + $this->session->getEntityEventBroadcaster()->syncAttributes([$this->session], $this->player, $this->player->getAttributeMap()->getAll()); $this->session->getLogger()->debug("Sending available commands"); $this->session->syncAvailableCommands(); @@ -125,7 +125,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->session->getLogger()->debug("Sending effects"); foreach($this->player->getEffects()->all() as $effect){ - $this->session->onEntityEffectAdded($this->player, $effect, false); + $this->session->getEntityEventBroadcaster()->onEntityEffectAdded([$this->session], $this->player, $effect, false); } $this->session->getLogger()->debug("Sending actor metadata"); diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php index b0d1bc13d..6092ac42c 100644 --- a/src/network/mcpe/raklib/RakLibInterface.php +++ b/src/network/mcpe/raklib/RakLibInterface.php @@ -26,14 +26,13 @@ namespace pocketmine\network\mcpe\raklib; use pocketmine\lang\KnownTranslationFactory; use pocketmine\network\AdvancedNetworkInterface; use pocketmine\network\mcpe\compression\ZlibCompressor; -use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary; use pocketmine\network\mcpe\convert\TypeConverter; +use pocketmine\network\mcpe\EntityEventBroadcaster; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\PacketBroadcaster; use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; -use pocketmine\network\mcpe\StandardPacketBroadcaster; use pocketmine\network\Network; use pocketmine\network\NetworkInterfaceStartException; use pocketmine\network\PacketHandlingException; @@ -80,11 +79,16 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ private SleeperNotifier $sleeper; - private PacketBroadcaster $broadcaster; + private PacketBroadcaster $packetBroadcaster; + private EntityEventBroadcaster $entityEventBroadcaster; private PacketSerializerContext $packetSerializerContext; - public function __construct(Server $server, string $ip, int $port, bool $ipV6){ + public function __construct(Server $server, string $ip, int $port, bool $ipV6, PacketBroadcaster $packetBroadcaster, EntityEventBroadcaster $entityEventBroadcaster, PacketSerializerContext $packetSerializerContext){ $this->server = $server; + $this->packetBroadcaster = $packetBroadcaster; + $this->packetSerializerContext = $packetSerializerContext; + $this->entityEventBroadcaster = $entityEventBroadcaster; + $this->rakServerId = mt_rand(0, PHP_INT_MAX); $this->sleeper = new SleeperNotifier(); @@ -110,9 +114,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ $this->interface = new UserToRakLibThreadMessageSender( new PthreadsChannelWriter($mainToThreadBuffer) ); - - $this->packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()); - $this->broadcaster = new StandardPacketBroadcaster($this->server, $this->packetSerializerContext); } public function start() : void{ @@ -179,7 +180,8 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ PacketPool::getInstance(), $this->packetSerializerContext, new RakLibPacketSender($sessionId, $this), - $this->broadcaster, + $this->packetBroadcaster, + $this->entityEventBroadcaster, ZlibCompressor::getInstance(), //TODO: this shouldn't be hardcoded, but we might need the RakNet protocol version to select it $address, $port diff --git a/src/world/World.php b/src/world/World.php index fb9949324..0ca67023a 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -66,6 +66,7 @@ use pocketmine\math\Vector3; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\convert\RuntimeBlockMapping; +use pocketmine\network\mcpe\NetworkBroadcastUtils; use pocketmine\network\mcpe\protocol\BlockActorDataPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; @@ -689,7 +690,7 @@ class World implements ChunkManager{ $this->broadcastPacketToViewers($pos, $e); } }else{ - $this->server->broadcastPackets($this->filterViewersForPosition($pos, $players), $pk); + NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $players), $pk); } } } @@ -715,7 +716,7 @@ class World implements ChunkManager{ $this->broadcastPacketToViewers($pos, $e); } }else{ - $this->server->broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk); + NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk); } } } @@ -1029,7 +1030,7 @@ class World implements ChunkManager{ World::getXZ($index, $chunkX, $chunkZ); $chunkPlayers = $this->getChunkPlayers($chunkX, $chunkZ); if(count($chunkPlayers) > 0){ - $this->server->broadcastPackets($chunkPlayers, $entries); + NetworkBroadcastUtils::broadcastPackets($chunkPlayers, $entries); } } diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 9213ff96d..99d24909c 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -485,11 +485,6 @@ parameters: count: 1 path: ../../../src/command/defaults/TimingsCommand.php - - - message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#" - count: 2 - path: ../../../src/console/ConsoleReader.php - - message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#" count: 1 @@ -645,21 +640,6 @@ parameters: count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - - message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" - count: 1 - path: ../../../src/network/mcpe/NetworkSession.php - - - - message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" - count: 1 - path: ../../../src/network/mcpe/NetworkSession.php - - - - message: "#^Parameter \\#1 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAttributes\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" - count: 1 - path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAbilities\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" count: 2 @@ -680,6 +660,21 @@ parameters: count: 1 path: ../../../src/network/mcpe/NetworkSession.php + - + message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + count: 1 + path: ../../../src/network/mcpe/NetworkSession.php + + - + message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + count: 1 + path: ../../../src/network/mcpe/NetworkSession.php + + - + message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:syncAttributes\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + count: 1 + path: ../../../src/network/mcpe/NetworkSession.php + - message: "#^Parameter \\#2 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" count: 1