mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
17 Commits
4.17.2
...
4.18.0-ALP
Author | SHA1 | Date | |
---|---|---|---|
bd21feffc4 | |||
5b324f695c | |||
ef45180b80 | |||
941fd03998 | |||
1af8da3c1f | |||
a5985dcf7d | |||
183d1f4038 | |||
08ee825d91 | |||
337a254768 | |||
a31e3331fd | |||
cc8660629b | |||
e7e19abe85 | |||
14f141fab2 | |||
daff955bc4 | |||
0022d82779 | |||
8e280ebb8b | |||
fa7c38276c |
45
changelogs/4.18-alpha.md
Normal file
45
changelogs/4.18-alpha.md
Normal file
@ -0,0 +1,45 @@
|
||||
**For Minecraft: Bedrock Edition 1.19.70**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the `pocketmine\network\mcpe` namespace are compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
### Alpha release warning
|
||||
Alpha releases are **experimental**. Features introduced in these releases are subject to change or removal.
|
||||
|
||||
APIs which existed **prior** to this version will continue to work as normal, so plugins which use them will continue to work.
|
||||
|
||||
### Highlights
|
||||
This version makes changes to the internal network system to improve server performance and reduce memory usage.
|
||||
|
||||
While these changes don't affect non-internal API, they are still significant enough to warrant a new minor version, as they may break plugins which use the internal network API (not recommended).
|
||||
|
||||
# 4.18.0-ALPHA1
|
||||
Released 16th March 2023.
|
||||
|
||||
## General
|
||||
- Improved server performance in congested areas of the world (lots of players and/or entities in the same area).
|
||||
|
||||
## API
|
||||
### `pocketmine\event\server`
|
||||
- The following new classes have been added:
|
||||
- `DataPacketDecodeEvent` - called before a packet is decoded by a `NetworkSession`; useful to mitigate DoS attacks if PocketMine-MP hasn't been patched against new bugs yet
|
||||
|
||||
## Internals
|
||||
- Introduced new system for broadcasting entity events to network sessions.
|
||||
- This change improves performance when lots of players and/or entities are in the same area.
|
||||
- New interface `EntityEventBroadcaster` and class `StandardEntityEventBroadcaster` have been added to implement this.
|
||||
- All entity-specific `on*()` and `sync*()` methods have been removed from `NetworkSession` (BC break).
|
||||
- `NetworkSession` now accepts an `EntityEventBroadcaster` instance in its constructor.
|
||||
- `NetworkBroadcastUtils::broadcastEntityEvent()` can be used to efficiently broadcast events to unique broadcasters shared by several network sessions.
|
||||
- All network sessions now share the same `PacketSerializerContext` and `PacketBroadcaster` by default.
|
||||
- Previously, every session had its own context, meaning that broadcast optimisations were not used, causing significant performance losses compared to 3.x.
|
||||
- This change improves performance in congested areas by allowing reuse of previously encoded packet buffers for all sessions sharing the same context.
|
||||
- Packet broadcasts are automatically encoded separately per unique `PacketSerializerContext` instance. This allows, for example, a multi-version fork to have a separate context for each protocol version, to ensure maximum broadcast efficiency while encoding different packets for different versions.
|
||||
- `PacketSerializerContext` is now passed in `NetworkSession::__construct()`, instead of being created by the session.
|
||||
- `StandardPacketBroadcaster` is now locked to a single `PacketSerializer` context, reducing complexity.
|
||||
- Introduced `NetworkBroadcastUtils::broadcastPackets()`, replacing `Server->broadcastPackets()`.
|
||||
- `Server->broadcastPackets()` has been deprecated. It will be removed in a future version.
|
@ -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;
|
||||
@ -1169,10 +1174,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,
|
||||
@ -1198,11 +1211,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;
|
||||
@ -1334,46 +1351,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();
|
||||
|
||||
/** @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,9 +31,9 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.17.1";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
public const BASE_VERSION = "4.18.0-ALPHA1";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "alpha";
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
|
@ -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;
|
||||
@ -785,7 +787,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,
|
||||
@ -800,7 +802,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{
|
||||
@ -1537,7 +1539,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]);
|
||||
}
|
||||
@ -1548,9 +1550,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 = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1624,9 +1628,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));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1680,7 +1682,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1689,7 +1691,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,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;
|
||||
@ -174,7 +176,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))
|
||||
]);
|
||||
}
|
||||
@ -189,9 +191,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{
|
||||
@ -270,11 +273,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()){
|
||||
@ -315,11 +317,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){
|
||||
@ -333,11 +334,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()));
|
||||
@ -490,11 +490,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(),
|
||||
@ -524,11 +525,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)]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -143,13 +145,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();
|
||||
|
||||
@ -850,7 +849,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{
|
||||
|
@ -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;
|
||||
@ -328,9 +330,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){
|
||||
|
@ -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;
|
||||
@ -196,9 +198,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();
|
||||
|
54
src/event/server/DataPacketDecodeEvent.php
Normal file
54
src/event/server/DataPacketDecodeEvent.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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\event\server;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
/**
|
||||
* Called before a packet is decoded and handled by the network session.
|
||||
* Cancelling this event will drop the packet without decoding it, minimizing wasted CPU time.
|
||||
*/
|
||||
class DataPacketDecodeEvent extends ServerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
private NetworkSession $origin,
|
||||
private int $packetId,
|
||||
private string $packetBuffer
|
||||
){}
|
||||
|
||||
public function getOrigin() : NetworkSession{
|
||||
return $this->origin;
|
||||
}
|
||||
|
||||
public function getPacketId() : int{
|
||||
return $this->packetId;
|
||||
}
|
||||
|
||||
public function getPacketBuffer() : string{
|
||||
return $this->packetBuffer;
|
||||
}
|
||||
}
|
93
src/network/mcpe/EntityEventBroadcaster.php
Normal file
93
src/network/mcpe/EntityEventBroadcaster.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?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\entity\Attribute;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Human;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
|
||||
|
||||
/**
|
||||
* This class allows broadcasting entity events to many viewers on the server network.
|
||||
*/
|
||||
interface EntityEventBroadcaster{
|
||||
|
||||
/**
|
||||
* @param NetworkSession[] $recipients
|
||||
* @param Attribute[] $attributes
|
||||
*/
|
||||
public function syncAttributes(array $recipients, Living $entity, array $attributes) : void;
|
||||
|
||||
/**
|
||||
* @param NetworkSession[] $recipients
|
||||
* @param MetadataProperty[] $properties
|
||||
*
|
||||
* @phpstan-param array<int, MetadataProperty> $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;
|
||||
}
|
103
src/network/mcpe/NetworkBroadcastUtils.php
Normal file
103
src/network/mcpe/NetworkBroadcastUtils.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?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\ClientboundPacket;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\timings\Timings;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
final class NetworkBroadcastUtils{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[] $recipients
|
||||
* @param ClientboundPacket[] $packets
|
||||
*/
|
||||
public static function broadcastPackets(array $recipients, array $packets) : bool{
|
||||
if(count($packets) === 0){
|
||||
throw new \InvalidArgumentException("Cannot broadcast empty list of packets");
|
||||
}
|
||||
|
||||
return Timings::$broadcastPackets->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();
|
||||
|
||||
/** @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<int, NetworkSession>) : 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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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\DataPacketDecodeEvent;
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\form\Form;
|
||||
@ -43,7 +39,6 @@ use pocketmine\network\mcpe\cache\ChunkCache;
|
||||
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
||||
use pocketmine\network\mcpe\compression\Compressor;
|
||||
use pocketmine\network\mcpe\compression\DecompressionException;
|
||||
use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\encryption\DecryptionException;
|
||||
@ -62,10 +57,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;
|
||||
@ -74,19 +65,16 @@ use pocketmine\network\mcpe\protocol\PacketDecodeException;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
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;
|
||||
@ -98,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;
|
||||
@ -135,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 strcasecmp;
|
||||
use function strlen;
|
||||
@ -144,7 +125,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
|
||||
@ -188,8 +168,6 @@ class NetworkSession{
|
||||
private bool $forceAsyncCompression = true;
|
||||
private bool $enableCompression = false; //disabled until handshake completed
|
||||
|
||||
private PacketSerializerContext $packetSerializerContext;
|
||||
|
||||
private ?InventoryManager $invManager = null;
|
||||
|
||||
/**
|
||||
@ -202,8 +180,10 @@ class NetworkSession{
|
||||
private Server $server,
|
||||
private NetworkSessionManager $manager,
|
||||
private PacketPool $packetPool,
|
||||
private PacketSerializerContext $packetSerializerContext,
|
||||
private PacketSender $sender,
|
||||
private PacketBroadcaster $broadcaster,
|
||||
private EntityEventBroadcaster $entityEventBroadcaster,
|
||||
private Compressor $compressor,
|
||||
private string $ip,
|
||||
private int $port
|
||||
@ -212,9 +192,6 @@ class NetworkSession{
|
||||
|
||||
$this->compressedQueue = new \SplQueue();
|
||||
|
||||
//TODO: allow this to be injected
|
||||
$this->packetSerializerContext = new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary());
|
||||
|
||||
$this->disposeHooks = new ObjectSet();
|
||||
|
||||
$this->connectTime = time();
|
||||
@ -278,10 +255,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);
|
||||
@ -435,6 +412,12 @@ class NetworkSession{
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
|
||||
$decodeTimings->startTiming();
|
||||
try{
|
||||
@ -452,18 +435,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 +535,8 @@ class NetworkSession{
|
||||
|
||||
public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; }
|
||||
|
||||
public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; }
|
||||
|
||||
public function getCompressor() : Compressor{
|
||||
return $this->compressor;
|
||||
}
|
||||
@ -822,7 +807,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);
|
||||
@ -931,41 +916,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<int, MetadataProperty> $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){
|
||||
@ -1101,36 +1051,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
|
||||
*/
|
||||
@ -1174,10 +1094,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));
|
||||
}
|
||||
@ -1217,7 +1133,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
|
||||
|
143
src/network/mcpe/StandardEntityEventBroadcaster.php
Normal file
143
src/network/mcpe/StandardEntityEventBroadcaster.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?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\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\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
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\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
|
||||
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\UpdateAttributesPacket;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function ksort;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
|
||||
|
||||
public function __construct(
|
||||
private StandardPacketBroadcaster $broadcaster
|
||||
){}
|
||||
|
||||
/**
|
||||
* @param NetworkSession[] $recipients
|
||||
*/
|
||||
private function sendDataPacket(array $recipients, ClientboundPacket $packet) : void{
|
||||
$this->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));
|
||||
}
|
||||
}
|
@ -25,62 +25,65 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
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){}
|
||||
public function __construct(
|
||||
private Server $server,
|
||||
private PacketSerializerContext $protocolContext
|
||||
){}
|
||||
|
||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||
$packetBufferTotalLengths = [];
|
||||
$packetBuffers = [];
|
||||
$compressors = [];
|
||||
/** @var NetworkSession[][][] $targetMap */
|
||||
$targetMap = [];
|
||||
|
||||
/** @var NetworkSession[][] $targetsByCompressor */
|
||||
$targetsByCompressor = [];
|
||||
foreach($recipients as $recipient){
|
||||
$serializerContext = $recipient->getPacketSerializerContext();
|
||||
$bufferId = spl_object_id($serializerContext);
|
||||
if(!isset($packetBuffers[$bufferId])){
|
||||
$packetBufferTotalLengths[$bufferId] = 0;
|
||||
$packetBuffers[$bufferId] = [];
|
||||
foreach($packets as $packet){
|
||||
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($serializerContext), $packet);
|
||||
$packetBufferTotalLengths[$bufferId] += strlen($buffer);
|
||||
$packetBuffers[$bufferId][] = $buffer;
|
||||
}
|
||||
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;
|
||||
|
||||
$targetMap[$bufferId][spl_object_id($compressor)][] = $recipient;
|
||||
$targetsByCompressor[spl_object_id($compressor)][] = $recipient;
|
||||
}
|
||||
|
||||
foreach($targetMap as $bufferId => $compressorMap){
|
||||
foreach($compressorMap as $compressorId => $compressorTargets){
|
||||
$compressor = $compressors[$compressorId];
|
||||
$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;
|
||||
}
|
||||
|
||||
$threshold = $compressor->getCompressionThreshold();
|
||||
if(count($compressorTargets) > 1 && $threshold !== null && $packetBufferTotalLengths[$bufferId] >= $threshold){
|
||||
//do not prepare shared batch unless we're sure it will be compressed
|
||||
$stream = new BinaryStream();
|
||||
PacketBatch::encodeRaw($stream, $packetBuffers[$bufferId]);
|
||||
$batchBuffer = $stream->getBuffer();
|
||||
foreach($targetsByCompressor as $compressorId => $compressorTargets){
|
||||
$compressor = $compressors[$compressorId];
|
||||
|
||||
$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[$bufferId] as $packetBuffer){
|
||||
$target->addToSendBuffer($packetBuffer);
|
||||
}
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\data\bedrock\LegacyBlockIdToStringIdMap;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
|
||||
@ -55,14 +54,14 @@ final class RuntimeBlockMapping{
|
||||
}
|
||||
|
||||
public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){
|
||||
$stream = PacketSerializer::decoder(
|
||||
Filesystem::fileGetContents($canonicalBlockStatesFile),
|
||||
0,
|
||||
new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary())
|
||||
);
|
||||
$stream = new BinaryStream(Filesystem::fileGetContents($canonicalBlockStatesFile));
|
||||
$list = [];
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
while(!$stream->feof()){
|
||||
$list[] = $stream->getNbtCompoundRoot();
|
||||
$offset = $stream->getOffset();
|
||||
$blockState = $nbtReader->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$stream->setOffset($offset);
|
||||
$list[] = $blockState;
|
||||
}
|
||||
$this->bedrockKnownStates = $list;
|
||||
|
||||
@ -73,14 +72,10 @@ final class RuntimeBlockMapping{
|
||||
$legacyIdMap = LegacyBlockIdToStringIdMap::getInstance();
|
||||
/** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */
|
||||
$legacyStateMap = [];
|
||||
$legacyStateMapReader = PacketSerializer::decoder(
|
||||
Filesystem::fileGetContents($r12ToCurrentBlockMapFile),
|
||||
0,
|
||||
new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary())
|
||||
);
|
||||
$legacyStateMapReader = new BinaryStream(Filesystem::fileGetContents($r12ToCurrentBlockMapFile));
|
||||
$nbtReader = new NetworkNbtSerializer();
|
||||
while(!$legacyStateMapReader->feof()){
|
||||
$id = $legacyStateMapReader->getString();
|
||||
$id = $legacyStateMapReader->get($legacyStateMapReader->getUnsignedVarInt());
|
||||
$meta = $legacyStateMapReader->getLShort();
|
||||
|
||||
$offset = $legacyStateMapReader->getOffset();
|
||||
|
@ -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");
|
||||
|
@ -26,11 +26,12 @@ namespace pocketmine\network\mcpe\raklib;
|
||||
use pocketmine\network\AdvancedNetworkInterface;
|
||||
use pocketmine\network\mcpe\compression\ZlibCompressor;
|
||||
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\StandardPacketBroadcaster;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\NetworkInterfaceStartException;
|
||||
use pocketmine\network\PacketHandlingException;
|
||||
@ -78,10 +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();
|
||||
@ -105,8 +112,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$this->interface = new UserToRakLibThreadMessageSender(
|
||||
new PthreadsChannelWriter($mainToThreadBuffer)
|
||||
);
|
||||
|
||||
$this->broadcaster = new StandardPacketBroadcaster($this->server);
|
||||
}
|
||||
|
||||
public function start() : void{
|
||||
@ -166,8 +171,10 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$this->server,
|
||||
$this->network->getSessionManager(),
|
||||
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
|
||||
|
@ -65,6 +65,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;
|
||||
@ -685,7 +686,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,7 +712,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1021,7 +1022,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,11 +490,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
|
||||
@ -650,21 +645,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
|
||||
@ -685,6 +665,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
|
||||
|
Reference in New Issue
Block a user