From 4b73bedd57ed73c2986956386e62676ba2cd586f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 1 Jun 2022 19:45:20 +0100 Subject: [PATCH 01/53] Bump version to 4.6.0+dev --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 5c9d27b32..f9a55d173 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,9 +31,9 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.4.1"; + public const BASE_VERSION = "4.6.0"; public const IS_DEVELOPMENT_BUILD = true; - public const BUILD_CHANNEL = "stable"; + public const BUILD_CHANNEL = "beta"; private function __construct(){ //NOOP From 35fd71eddfa014a1ec4aaad960d964e80f4b0210 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jun 2022 20:26:02 +0100 Subject: [PATCH 02/53] Fix CS in VersionInfo this got overlooked because I just nuked the changes coming in from stable and didn't notice the formatting changes. --- src/VersionInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index f9a55d173..ff5c8e0f7 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -17,7 +17,7 @@ * @link http://www.pocketmine.net/ * * -*/ + */ declare(strict_types=1); From e2855aadffa5600f3c85eecfe5274d8d78720e32 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Jul 2022 20:47:38 +0100 Subject: [PATCH 03/53] Simplify handling of broken transactions for crafting and friends this allows stuff like smithing tables to work without needing any extra hacks. Implementing enchanting or anvils would require some extra work, but I don't plan to implement those under the legacy transaction system anyway. --- src/network/mcpe/convert/TypeConverter.php | 13 ++++--------- src/network/mcpe/handler/InGamePacketHandler.php | 7 +------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php index 2a74c9f07..424f5d875 100644 --- a/src/network/mcpe/convert/TypeConverter.php +++ b/src/network/mcpe/convert/TypeConverter.php @@ -326,15 +326,10 @@ class TypeConverter{ } case NetworkInventoryAction::SOURCE_TODO: - //These types need special handling. - switch($action->windowId){ - case NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT: - case NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT: - return null; - } - - //TODO: more stuff - throw new TypeConversionException("No open container with window ID $action->windowId"); + //These are used to balance a transaction that involves special actions, like crafting, enchanting, etc. + //The vanilla server just accepted these without verifying them. We don't need to care about them since + //we verify crafting by checking for imbalances anyway. + return null; default: throw new TypeConversionException("Unknown inventory source type $action->sourceType"); } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 83c2d03cf..8fd6894f9 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -319,12 +319,7 @@ class InGamePacketHandler extends PacketHandler{ $converter = TypeConverter::getInstance(); foreach($data->getActions() as $networkInventoryAction){ if( - ( - $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO && ( - $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT || - $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT - ) - ) || ( + $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO || ( $this->craftingTransaction !== null && !$networkInventoryAction->oldItem->getItemStack()->equals($networkInventoryAction->newItem->getItemStack()) && $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && From 4e3964ffce7cf9c8447d24994db7f94c563d4b2b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Jul 2022 15:10:07 +0100 Subject: [PATCH 04/53] Armor: added clearCustomColor() --- src/item/Armor.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/item/Armor.php b/src/item/Armor.php index 8d77db8dc..25374c6a0 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -86,6 +86,12 @@ class Armor extends Durable{ return $this; } + /** @return $this */ + public function clearCustomColor() : self{ + $this->customColor = null; + return $this; + } + /** * Returns the total enchantment protection factor this armour piece offers from all applicable protection * enchantments on the item. From 24e72ec109c1442b09558df89b6833cf2f2e0ec7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Jul 2022 20:39:37 +0100 Subject: [PATCH 05/53] ContainerTrait: improve performance of block destroy hook this was creating useless vector3s for every iteration, as well as repeatedly invoking Position->getWorld() for no reason. --- src/block/tile/ContainerTrait.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/block/tile/ContainerTrait.php b/src/block/tile/ContainerTrait.php index 43107cd6a..4c4907a9b 100644 --- a/src/block/tile/ContainerTrait.php +++ b/src/block/tile/ContainerTrait.php @@ -96,8 +96,10 @@ trait ContainerTrait{ $inv = $this->getRealInventory(); $pos = $this->getPosition(); + $world = $pos->getWorld(); + $dropPos = $pos->add(0.5, 0.5, 0.5); foreach($inv->getContents() as $k => $item){ - $pos->getWorld()->dropItem($pos->add(0.5, 0.5, 0.5), $item); + $world->dropItem($dropPos, $item); } $inv->clearAll(); } From 2940547026db40ce76deb46e992870de3ead79ad Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Jul 2022 20:44:05 +0100 Subject: [PATCH 06/53] Eliminate repeated calls to Position->getWorld() as well as improving readability, it also improves performance in some areas. --- src/block/Bamboo.php | 7 ++--- src/block/BambooSapling.php | 5 ++-- src/block/BaseRail.php | 10 ++++--- src/block/Bell.php | 7 ++--- src/block/Block.php | 14 +++++----- src/block/BrewingStand.php | 7 ++--- src/block/Button.php | 12 +++++---- src/block/Cactus.php | 16 +++++++----- src/block/Chest.php | 4 +-- src/block/DaylightSensor.php | 10 ++++--- src/block/Dirt.php | 5 ++-- src/block/Door.php | 7 ++--- src/block/DragonEgg.php | 9 ++++--- src/block/Farmland.php | 7 ++--- src/block/FenceGate.php | 5 ++-- src/block/Fire.php | 15 ++++++----- src/block/FlowerPot.php | 7 ++--- src/block/FrostedIce.php | 17 +++++++----- src/block/Furnace.php | 7 ++--- src/block/Grass.php | 26 ++++++++++--------- src/block/Ice.php | 5 ++-- src/block/ItemFrame.php | 5 ++-- src/block/Leaves.php | 5 ++-- src/block/Lectern.php | 15 ++++++----- src/block/Lever.php | 5 ++-- src/block/Liquid.php | 12 +++++---- src/block/Mycelium.php | 5 ++-- src/block/Pumpkin.php | 5 ++-- src/block/Sapling.php | 5 ++-- src/block/SnowLayer.php | 5 ++-- src/block/Stem.php | 5 ++-- src/block/Sugarcane.php | 9 ++++--- src/block/SweetBerryBush.php | 7 ++--- src/block/TNT.php | 5 ++-- src/block/Trapdoor.php | 5 ++-- src/block/Vine.php | 5 ++-- src/block/inventory/BarrelInventory.php | 5 ++-- src/block/utils/FallableTrait.php | 7 ++--- .../mcpe/handler/PreSpawnPacketHandler.php | 7 ++--- 39 files changed, 185 insertions(+), 134 deletions(-) diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php index 427b0b8e7..e33022824 100644 --- a/src/block/Bamboo.php +++ b/src/block/Bamboo.php @@ -164,9 +164,10 @@ class Bamboo extends Transparent{ } public function onNearbyBlockChange() : void{ - $below = $this->position->getWorld()->getBlock($this->position->down()); + $world = $this->position->getWorld(); + $below = $world->getBlock($this->position->down()); if(!$this->canBeSupportedBy($below) && !$below->isSameType($this)){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); } } @@ -212,7 +213,7 @@ class Bamboo extends Transparent{ } } - $tx = new BlockTransaction($this->position->getWorld()); + $tx = new BlockTransaction($world); foreach($newBlocks as $idx => $newBlock){ $tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock); } diff --git a/src/block/BambooSapling.php b/src/block/BambooSapling.php index a00361416..af3e65364 100644 --- a/src/block/BambooSapling.php +++ b/src/block/BambooSapling.php @@ -82,8 +82,9 @@ final class BambooSapling extends Flowable{ } public function onNearbyBlockChange() : void{ - if(!$this->canBeSupportedBy($this->position->getWorld()->getBlock($this->position->down()))){ - $this->position->getWorld()->useBreakOn($this->position); + $world = $this->position->getWorld(); + if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){ + $world->useBreakOn($this->position); } } diff --git a/src/block/BaseRail.php b/src/block/BaseRail.php index 933763122..971beffac 100644 --- a/src/block/BaseRail.php +++ b/src/block/BaseRail.php @@ -162,6 +162,7 @@ abstract class BaseRail extends Flowable{ $thisConnections = $this->getConnectedDirections(); $changed = false; + $world = $this->position->getWorld(); do{ $possible = $this->getPossibleConnectionDirections($thisConnections); $continue = false; @@ -189,7 +190,7 @@ abstract class BaseRail extends Flowable{ if(isset($otherPossible[$otherSide])){ $otherConnections[] = $otherSide; $other->setConnections($otherConnections); - $this->position->getWorld()->setBlock($other->position, $other); + $world->setBlock($other->position, $other); $changed = true; $thisConnections[] = $thisSide; @@ -202,7 +203,7 @@ abstract class BaseRail extends Flowable{ if($changed){ $this->setConnections($thisConnections); - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } } @@ -220,12 +221,13 @@ abstract class BaseRail extends Flowable{ } public function onNearbyBlockChange() : void{ + $world = $this->position->getWorld(); if(!$this->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); }else{ foreach($this->getCurrentShapeConnections() as $connection){ if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); break; } } diff --git a/src/block/Bell.php b/src/block/Bell.php index c75d7d785..70c78d027 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -179,10 +179,11 @@ final class Bell extends Transparent{ } public function ring(int $faceHit) : void{ - $this->position->getWorld()->addSound($this->position, new BellRingSound()); - $tile = $this->position->getWorld()->getTile($this->position); + $world = $this->position->getWorld(); + $world->addSound($this->position, new BellRingSound()); + $tile = $world->getTile($this->position); if($tile instanceof TileBell){ - $this->position->getWorld()->broadcastPacketToViewers($this->position, $tile->createFakeUpdatePacket($faceHit)); + $world->broadcastPacketToViewers($this->position, $tile->createFakeUpdatePacket($faceHit)); } } } diff --git a/src/block/Block.php b/src/block/Block.php index a7839e8ee..b68080720 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -144,10 +144,11 @@ class Block{ } public function writeStateToWorld() : void{ - $this->position->getWorld()->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getFullId()); + $world = $this->position->getWorld(); + $world->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getFullId()); $tileType = $this->idInfo->getTileClass(); - $oldTile = $this->position->getWorld()->getTile($this->position); + $oldTile = $world->getTile($this->position); if($oldTile !== null){ if($tileType === null || !($oldTile instanceof $tileType)){ $oldTile->close(); @@ -161,8 +162,8 @@ class Block{ * @var Tile $tile * @see Tile::__construct() */ - $tile = new $tileType($this->position->getWorld(), $this->position->asVector3()); - $this->position->getWorld()->addTile($tile); + $tile = new $tileType($world, $this->position->asVector3()); + $world->addTile($tile); } } @@ -229,10 +230,11 @@ class Block{ * Do the actions needed so the block is broken with the Item */ public function onBreak(Item $item, ?Player $player = null) : bool{ - if(($t = $this->position->getWorld()->getTile($this->position)) !== null){ + $world = $this->position->getWorld(); + if(($t = $world->getTile($this->position)) !== null){ $t->onBlockDestroyed(); } - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); + $world->setBlock($this->position, VanillaBlocks::AIR()); return true; } diff --git a/src/block/BrewingStand.php b/src/block/BrewingStand.php index c1840502e..19d239f82 100644 --- a/src/block/BrewingStand.php +++ b/src/block/BrewingStand.php @@ -130,10 +130,11 @@ class BrewingStand extends Transparent{ } public function onScheduledUpdate() : void{ - $brewing = $this->position->getWorld()->getTile($this->position); + $world = $this->position->getWorld(); + $brewing = $world->getTile($this->position); if($brewing instanceof TileBrewingStand){ if($brewing->onUpdate()){ - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1); + $world->scheduleDelayedBlockUpdate($this->position, 1); } $changed = false; @@ -146,7 +147,7 @@ class BrewingStand extends Transparent{ } if($changed){ - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } } } diff --git a/src/block/Button.php b/src/block/Button.php index 967e0d4f0..c447ddb8d 100644 --- a/src/block/Button.php +++ b/src/block/Button.php @@ -73,9 +73,10 @@ abstract class Button extends Flowable{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->pressed){ $this->pressed = true; - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, $this->getActivationTime()); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOnSound()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this); + $world->scheduleDelayedBlockUpdate($this->position, $this->getActivationTime()); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOnSound()); } return true; @@ -84,8 +85,9 @@ abstract class Button extends Flowable{ public function onScheduledUpdate() : void{ if($this->pressed){ $this->pressed = false; - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound()); } } diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 987bea144..d74e69c08 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -88,13 +88,14 @@ class Cactus extends Transparent{ public function onNearbyBlockChange() : void{ $down = $this->getSide(Facing::DOWN); + $world = $this->position->getWorld(); if($down->getId() !== BlockLegacyIds::SAND && !$down->isSameType($this)){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); }else{ foreach(Facing::HORIZONTAL as $side){ $b = $this->getSide($side); if($b->isSolid()){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); break; } } @@ -107,28 +108,29 @@ class Cactus extends Transparent{ public function onRandomTick() : void{ if(!$this->getSide(Facing::DOWN)->isSameType($this)){ + $world = $this->position->getWorld(); if($this->age === self::MAX_AGE){ for($y = 1; $y < 3; ++$y){ - if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){ + if(!$world->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){ break; } - $b = $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); + $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); if($b->getId() === BlockLegacyIds::AIR){ $ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS()); $ev->call(); if($ev->isCancelled()){ break; } - $this->position->getWorld()->setBlock($b->position, $ev->getNewState()); + $world->setBlock($b->position, $ev->getNewState()); }else{ break; } } $this->age = 0; - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); }else{ ++$this->age; - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } } } diff --git a/src/block/Chest.php b/src/block/Chest.php index 59c21e1b6..9e012ee39 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -51,13 +51,13 @@ class Chest extends Transparent{ } public function onPostPlace() : void{ - $tile = $this->position->getWorld()->getTile($this->position); + $world = $this->position->getWorld(); + $tile = $world->getTile($this->position); if($tile instanceof TileChest){ foreach([false, true] as $clockwise){ $side = Facing::rotateY($this->facing, $clockwise); $c = $this->getSide($side); if($c instanceof Chest && $c->isSameType($this) && $c->facing === $this->facing){ - $world = $this->position->getWorld(); $pair = $world->getTile($c->position); if($pair instanceof TileChest && !$pair->isPaired()){ [$left, $right] = $clockwise ? [$c, $this] : [$this, $c]; diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index c13df4b6a..b9e8a58f4 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -100,21 +100,23 @@ class DaylightSensor extends Transparent{ } public function onScheduledUpdate() : void{ + $world = $this->position->getWorld(); $signalStrength = $this->recalculateSignalStrength(); if($this->signalStrength !== $signalStrength){ $this->signalStrength = $signalStrength; - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 20); + $world->scheduleDelayedBlockUpdate($this->position, 20); } private function recalculateSignalStrength() : int{ - $lightLevel = $this->position->getWorld()->getRealBlockSkyLightAt($this->position->x, $this->position->y, $this->position->z); + $world = $this->position->getWorld(); + $lightLevel = $world->getRealBlockSkyLightAt($this->position->x, $this->position->y, $this->position->z); if($this->inverted){ return 15 - $lightLevel; } - $sunAngle = $this->position->getWorld()->getSunAnglePercentage(); + $sunAngle = $world->getSunAnglePercentage(); return max(0, (int) round($lightLevel * cos(($sunAngle + ((($sunAngle < 0.5 ? 0 : 1) - $sunAngle) / 5)) * 2 * M_PI))); } diff --git a/src/block/Dirt.php b/src/block/Dirt.php index 8197f1415..cca195a64 100644 --- a/src/block/Dirt.php +++ b/src/block/Dirt.php @@ -63,8 +63,9 @@ class Dirt extends Opaque{ $item->applyDamage(1); $newBlock = $this->coarse ? VanillaBlocks::DIRT() : VanillaBlocks::FARMLAND(); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); - $this->position->getWorld()->setBlock($this->position, $newBlock); + $world = $this->position->getWorld(); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); + $world->setBlock($this->position, $newBlock); return true; } diff --git a/src/block/Door.php b/src/block/Door.php index 191a8fe0f..a0f2169b8 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -163,13 +163,14 @@ class Door extends Transparent{ $this->open = !$this->open; $other = $this->getSide($this->top ? Facing::DOWN : Facing::UP); + $world = $this->position->getWorld(); if($other instanceof Door && $other->isSameType($this)){ $other->open = $this->open; - $this->position->getWorld()->setBlock($other->position, $other); + $world->setBlock($other->position, $other); } - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->addSound($this->position, new DoorSound()); + $world->setBlock($this->position, $this); + $world->addSound($this->position, new DoorSound()); return true; } diff --git a/src/block/DragonEgg.php b/src/block/DragonEgg.php index db6512a9c..04b30dabe 100644 --- a/src/block/DragonEgg.php +++ b/src/block/DragonEgg.php @@ -62,8 +62,9 @@ class DragonEgg extends Transparent implements Fallable{ } public function teleport() : void{ + $world = $this->position->getWorld(); for($tries = 0; $tries < 16; ++$tries){ - $block = $this->position->getWorld()->getBlockAt( + $block = $world->getBlockAt( $this->position->x + mt_rand(-16, 16), max(World::Y_MIN, min(World::Y_MAX - 1, $this->position->y + mt_rand(-8, 8))), $this->position->z + mt_rand(-16, 16) @@ -76,9 +77,9 @@ class DragonEgg extends Transparent implements Fallable{ } $blockPos = $ev->getTo(); - $this->position->getWorld()->addParticle($this->position, new DragonEggTeleportParticle($this->position->x - $blockPos->x, $this->position->y - $blockPos->y, $this->position->z - $blockPos->z)); - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); - $this->position->getWorld()->setBlock($blockPos, $this); + $world->addParticle($this->position, new DragonEggTeleportParticle($this->position->x - $blockPos->x, $this->position->y - $blockPos->y, $this->position->z - $blockPos->z)); + $world->setBlock($this->position, VanillaBlocks::AIR()); + $world->setBlock($blockPos, $this); break; } } diff --git a/src/block/Farmland.php b/src/block/Farmland.php index 6e1ad13f0..57392300f 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -78,16 +78,17 @@ class Farmland extends Transparent{ } public function onRandomTick() : void{ + $world = $this->position->getWorld(); if(!$this->canHydrate()){ if($this->wetness > 0){ $this->wetness--; - $this->position->getWorld()->setBlock($this->position, $this, false); + $world->setBlock($this->position, $this, false); }else{ - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT()); + $world->setBlock($this->position, VanillaBlocks::DIRT()); } }elseif($this->wetness < self::MAX_WETNESS){ $this->wetness = self::MAX_WETNESS; - $this->position->getWorld()->setBlock($this->position, $this, false); + $world->setBlock($this->position, $this, false); } } diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index fefd49b6c..7d45a917e 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -117,8 +117,9 @@ class FenceGate extends Transparent{ } } - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->addSound($this->position, new DoorSound()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this); + $world->addSound($this->position, new DoorSound()); return true; } diff --git a/src/block/Fire.php b/src/block/Fire.php index b599f65ae..648724529 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -100,10 +100,11 @@ class Fire extends Flowable{ } public function onNearbyBlockChange() : void{ + $world = $this->position->getWorld(); if($this->getSide(Facing::DOWN)->isTransparent() && !$this->hasAdjacentFlammableBlocks()){ - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); + $world->setBlock($this->position, VanillaBlocks::AIR()); }else{ - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40)); + $world->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40)); } } @@ -136,11 +137,12 @@ class Fire extends Flowable{ } } + $world = $this->position->getWorld(); if($result !== null){ - $this->position->getWorld()->setBlock($this->position, $result); + $world->setBlock($this->position, $result); } - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40)); + $world->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40)); if($canSpread){ $this->burnBlocksAround(); @@ -181,7 +183,8 @@ class Fire extends Flowable{ if(!$ev->isCancelled()){ $block->onIncinerate(); - if($this->position->getWorld()->getBlock($block->getPosition())->isSameState($block)){ + $world = $this->position->getWorld(); + if($world->getBlock($block->getPosition())->isSameState($block)){ $spreadedFire = false; if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain $fire = clone $this; @@ -189,7 +192,7 @@ class Fire extends Flowable{ $spreadedFire = $this->spreadBlock($block, $fire); } if(!$spreadedFire){ - $this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR()); + $world->setBlock($block->position, VanillaBlocks::AIR()); } } } diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php index a60719675..5ebe6b8ba 100644 --- a/src/block/FlowerPot.php +++ b/src/block/FlowerPot.php @@ -122,6 +122,7 @@ class FlowerPot extends Flowable{ } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $world = $this->position->getWorld(); $plant = $item->getBlock(); if($this->plant !== null){ if($this->isValidPlant($plant)){ @@ -137,16 +138,16 @@ class FlowerPot extends Flowable{ $removedItems = $player->getInventory()->addItem(...$removedItems); } foreach($removedItems as $drops){ - $this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), $drops); + $world->dropItem($this->position->add(0.5, 0.5, 0.5), $drops); } $this->setPlant(null); - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); return true; }elseif($this->isValidPlant($plant)){ $this->setPlant($plant); $item->pop(); - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); return true; } diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 065caf589..95e15f3f7 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -56,16 +56,18 @@ class FrostedIce extends Ice{ } public function onNearbyBlockChange() : void{ + $world = $this->position->getWorld(); if(!$this->checkAdjacentBlocks(2)){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); }else{ - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); + $world->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); } } public function onRandomTick() : void{ + $world = $this->position->getWorld(); if((!$this->checkAdjacentBlocks(4) || mt_rand(0, 2) === 0) && - $this->position->getWorld()->getHighestAdjacentFullLightAt($this->position->x, $this->position->y, $this->position->z) >= 12 - $this->age){ + $world->getHighestAdjacentFullLightAt($this->position->x, $this->position->y, $this->position->z) >= 12 - $this->age){ if($this->tryMelt()){ foreach($this->getAllSides() as $block){ if($block instanceof FrostedIce){ @@ -74,7 +76,7 @@ class FrostedIce extends Ice{ } } }else{ - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); + $world->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); } } @@ -106,18 +108,19 @@ class FrostedIce extends Ice{ * @return bool Whether the ice was destroyed. */ private function tryMelt() : bool{ + $world = $this->position->getWorld(); if($this->age >= self::MAX_AGE){ $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + $world->setBlock($this->position, $ev->getNewState()); } return true; } $this->age++; - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); + $world->setBlock($this->position, $this); + $world->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40)); return false; } } diff --git a/src/block/Furnace.php b/src/block/Furnace.php index f5a81c524..d4b1ba9f1 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -83,12 +83,13 @@ class Furnace extends Opaque{ } public function onScheduledUpdate() : void{ - $furnace = $this->position->getWorld()->getTile($this->position); + $world = $this->position->getWorld(); + $furnace = $world->getTile($this->position); if($furnace instanceof TileFurnace && $furnace->onUpdate()){ if(mt_rand(1, 60) === 1){ //in vanilla this is between 1 and 5 seconds; try to average about 3 - $this->position->getWorld()->addSound($this->position, $furnace->getFurnaceType()->getCookSound()); + $world->addSound($this->position, $furnace->getFurnaceType()->getCookSound()); } - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1); //TODO: check this + $world->scheduleDelayedBlockUpdate($this->position, 1); //TODO: check this } } } diff --git a/src/block/Grass.php b/src/block/Grass.php index d0ea0520d..8ddbb7f0c 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -53,13 +53,14 @@ class Grass extends Opaque{ } public function onRandomTick() : void{ - $lightAbove = $this->position->getWorld()->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); - if($lightAbove < 4 && $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ + $world = $this->position->getWorld(); + $lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); + if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ //grass dies $ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState(), false); + $world->setBlock($this->position, $ev->getNewState(), false); } }elseif($lightAbove >= 9){ //try grass spread @@ -68,12 +69,12 @@ class Grass extends Opaque{ $y = mt_rand($this->position->y - 3, $this->position->y + 1); $z = mt_rand($this->position->z - 1, $this->position->z + 1); - $b = $this->position->getWorld()->getBlockAt($x, $y, $z); + $b = $world->getBlockAt($x, $y, $z); if( !($b instanceof Dirt) || $b->isCoarse() || - $this->position->getWorld()->getFullLightAt($x, $y + 1, $z) < 4 || - $this->position->getWorld()->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2 + $world->getFullLightAt($x, $y + 1, $z) < 4 || + $world->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2 ){ continue; } @@ -81,7 +82,7 @@ class Grass extends Opaque{ $ev = new BlockSpreadEvent($b, $this, VanillaBlocks::GRASS()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($b->position, $ev->getNewState(), false); + $world->setBlock($b->position, $ev->getNewState(), false); } } } @@ -91,23 +92,24 @@ class Grass extends Opaque{ if($face !== Facing::UP){ return false; } + $world = $this->position->getWorld(); if($item instanceof Fertilizer){ $item->pop(); - TallGrassObject::growGrass($this->position->getWorld(), $this->position, new Random(mt_rand()), 8, 2); + TallGrassObject::growGrass($world, $this->position, new Random(mt_rand()), 8, 2); return true; }elseif($item instanceof Hoe){ $item->applyDamage(1); $newBlock = VanillaBlocks::FARMLAND(); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); - $this->position->getWorld()->setBlock($this->position, $newBlock); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); + $world->setBlock($this->position, $newBlock); return true; }elseif($item instanceof Shovel && $this->getSide(Facing::UP)->getId() === BlockLegacyIds::AIR){ $item->applyDamage(1); $newBlock = VanillaBlocks::GRASS_PATH(); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); - $this->position->getWorld()->setBlock($this->position, $newBlock); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new ItemUseOnBlockSound($newBlock)); + $world->setBlock($this->position, $newBlock); return true; } diff --git a/src/block/Ice.php b/src/block/Ice.php index 7edbc2659..5f62fc032 100644 --- a/src/block/Ice.php +++ b/src/block/Ice.php @@ -51,11 +51,12 @@ class Ice extends Transparent{ } public function onRandomTick() : void{ - if($this->position->getWorld()->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ + $world = $this->position->getWorld(); + if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + $world->setBlock($this->position, $ev->getNewState()); } } } diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index 3e7a7378a..d71621943 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -151,11 +151,12 @@ class ItemFrame extends Flowable{ if($this->framedItem === null){ return false; } + $world = $this->position->getWorld(); if(lcg_value() <= $this->itemDropChance){ - $this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem); + $world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem); } $this->setFramedItem(null); - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); return true; } diff --git a/src/block/Leaves.php b/src/block/Leaves.php index 8e7298a0c..c5d2a5c2a 100644 --- a/src/block/Leaves.php +++ b/src/block/Leaves.php @@ -123,11 +123,12 @@ class Leaves extends Transparent{ if(!$this->noDecay && $this->checkDecay){ $ev = new LeavesDecayEvent($this); $ev->call(); + $world = $this->position->getWorld(); if($ev->isCancelled() || $this->findLog($this->position)){ $this->checkDecay = false; - $this->position->getWorld()->setBlock($this->position, $this, false); + $world->setBlock($this->position, $this, false); }else{ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); } } } diff --git a/src/block/Lectern.php b/src/block/Lectern.php index e94c5c706..1f339ad51 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -128,8 +128,9 @@ class Lectern extends Transparent{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->book === null && $item instanceof WritableBookBase){ - $this->position->getWorld()->setBlock($this->position, $this->setBook($item)); - $this->position->getWorld()->addSound($this->position, new LecternPlaceBookSound()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this->setBook($item)); + $world->addSound($this->position, new LecternPlaceBookSound()); $item->pop(); } return true; @@ -137,8 +138,9 @@ class Lectern extends Transparent{ public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ if($this->book !== null){ - $this->position->getWorld()->dropItem($this->position->up(), $this->book); - $this->position->getWorld()->setBlock($this->position, $this->setBook(null)); + $world = $this->position->getWorld(); + $world->dropItem($this->position->up(), $this->book); + $world->setBlock($this->position, $this->setBook(null)); } return false; } @@ -152,12 +154,13 @@ class Lectern extends Transparent{ } $this->viewedPage = $newPage; + $world = $this->position->getWorld(); if(!$this->producingSignal){ $this->producingSignal = true; - $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1); + $world->scheduleDelayedBlockUpdate($this->position, 1); } - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); return true; } diff --git a/src/block/Lever.php b/src/block/Lever.php index c9dfe76bb..07fbac45e 100644 --- a/src/block/Lever.php +++ b/src/block/Lever.php @@ -128,8 +128,9 @@ class Lever extends Flowable{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->activated = !$this->activated; - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->addSound( + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this); + $world->addSound( $this->position->add(0.5, 0.5, 0.5), $this->activated ? new RedstonePowerOnSound() : new RedstonePowerOffSound() ); diff --git a/src/block/Liquid.php b/src/block/Liquid.php index 64018b9df..10feaeecf 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -329,7 +329,7 @@ abstract class Liquid extends Transparent{ } if($adjacentDecay <= self::MAX_DECAY){ - $calculator = new MinimumCostFlowCalculator($this->position->getWorld(), $this->getFlowDecayPerBlock(), \Closure::fromCallable([$this, 'canFlowInto'])); + $calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), \Closure::fromCallable([$this, 'canFlowInto'])); foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){ $this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false); } @@ -348,11 +348,12 @@ abstract class Liquid extends Transparent{ $ev = new BlockSpreadEvent($block, $this, $new); $ev->call(); if(!$ev->isCancelled()){ + $world = $this->position->getWorld(); if($block->getId() !== BlockLegacyIds::AIR){ - $this->position->getWorld()->useBreakOn($block->position); + $world->useBreakOn($block->position); } - $this->position->getWorld()->setBlock($block->position, $ev->getNewState()); + $world->setBlock($block->position, $ev->getNewState()); } } } @@ -382,8 +383,9 @@ abstract class Liquid extends Transparent{ $ev = new BlockFormEvent($this, $result); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $ev->getNewState()); + $world->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); } return true; } diff --git a/src/block/Mycelium.php b/src/block/Mycelium.php index 11d00c5d8..f7e989c45 100644 --- a/src/block/Mycelium.php +++ b/src/block/Mycelium.php @@ -49,13 +49,14 @@ class Mycelium extends Opaque{ $x = mt_rand($this->position->x - 1, $this->position->x + 1); $y = mt_rand($this->position->y - 2, $this->position->y + 2); $z = mt_rand($this->position->z - 1, $this->position->z + 1); - $block = $this->position->getWorld()->getBlockAt($x, $y, $z); + $world = $this->position->getWorld(); + $block = $world->getBlockAt($x, $y, $z); if($block instanceof Dirt && !$block->isCoarse()){ if($block->getSide(Facing::UP) instanceof Transparent){ $ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($block->position, $ev->getNewState()); + $world->setBlock($block->position, $ev->getNewState()); } } } diff --git a/src/block/Pumpkin.php b/src/block/Pumpkin.php index 2f374670e..29dc196b7 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -36,8 +36,9 @@ class Pumpkin extends Opaque{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){ $item->applyDamage(1); - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face)); - $this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1)); + $world = $this->position->getWorld(); + $world->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face)); + $world->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1)); return true; } return false; diff --git a/src/block/Sapling.php b/src/block/Sapling.php index fb67dbc46..ea6f71d54 100644 --- a/src/block/Sapling.php +++ b/src/block/Sapling.php @@ -96,12 +96,13 @@ class Sapling extends Flowable{ } public function onRandomTick() : void{ - if($this->position->getWorld()->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 && mt_rand(1, 7) === 1){ + $world = $this->position->getWorld(); + if($world->getFullLightAt($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) >= 8 && mt_rand(1, 7) === 1){ if($this->ready){ $this->grow(null); }else{ $this->ready = true; - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } } } diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index 4e65b5eeb..40c4b3633 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -111,11 +111,12 @@ class SnowLayer extends Flowable implements Fallable{ } public function onRandomTick() : void{ - if($this->position->getWorld()->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ + $world = $this->position->getWorld(); + if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ $ev = new BlockMeltEvent($this, VanillaBlocks::AIR()); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + $world->setBlock($this->position, $ev->getNewState()); } } } diff --git a/src/block/Stem.php b/src/block/Stem.php index b17a5c646..7db17a8d4 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -35,13 +35,14 @@ abstract class Stem extends Crops{ public function onRandomTick() : void{ if(mt_rand(0, 2) === 1){ + $world = $this->position->getWorld(); if($this->age < self::MAX_AGE){ $block = clone $this; ++$block->age; $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + $world->setBlock($this->position, $ev->getNewState()); } }else{ $grow = $this->getPlant(); @@ -57,7 +58,7 @@ abstract class Stem extends Crops{ $ev = new BlockGrowEvent($side, $grow); $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($side->position, $ev->getNewState()); + $world->setBlock($side->position, $ev->getNewState()); } } } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index c2109645e..2af749fc4 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -51,25 +51,26 @@ class Sugarcane extends Flowable{ private function grow() : bool{ $grew = false; + $world = $this->position->getWorld(); for($y = 1; $y < 3; ++$y){ - if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){ + if(!$world->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){ break; } - $b = $this->position->getWorld()->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); + $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); if($b->getId() === BlockLegacyIds::AIR){ $ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE()); $ev->call(); if($ev->isCancelled()){ break; } - $this->position->getWorld()->setBlock($b->position, $ev->getNewState()); + $world->setBlock($b->position, $ev->getNewState()); $grew = true; }else{ break; } } $this->age = 0; - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); return $grew; } diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 5f1163bc8..0f18beba2 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -90,6 +90,7 @@ class SweetBerryBush extends Flowable{ } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $world = $this->position->getWorld(); if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; $block->age++; @@ -98,13 +99,13 @@ class SweetBerryBush extends Flowable{ $ev->call(); if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + $world->setBlock($this->position, $ev->getNewState()); $item->pop(); } }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ - $this->position->getWorld()->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); - $this->position->getWorld()->dropItem($this->position, $this->asItem()->setCount($dropAmount)); + $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); + $world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); } return true; diff --git a/src/block/TNT.php b/src/block/TNT.php index 561e36e37..6bd5178a3 100644 --- a/src/block/TNT.php +++ b/src/block/TNT.php @@ -110,11 +110,12 @@ class TNT extends Opaque{ } public function ignite(int $fuse = 80) : void{ - $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, VanillaBlocks::AIR()); $mot = (new Random())->nextSignedFloat() * M_PI * 2; - $tnt = new PrimedTNT(Location::fromObject($this->position->add(0.5, 0, 0.5), $this->position->getWorld())); + $tnt = new PrimedTNT(Location::fromObject($this->position->add(0.5, 0, 0.5), $world)); $tnt->setFuse($fuse); $tnt->setWorksUnderwater($this->worksUnderwater); $tnt->setMotion(new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02)); diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index 054737d74..6082aee51 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -96,8 +96,9 @@ class Trapdoor extends Transparent{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->open = !$this->open; - $this->position->getWorld()->setBlock($this->position, $this); - $this->position->getWorld()->addSound($this->position, new DoorSound()); + $world = $this->position->getWorld(); + $world->setBlock($this->position, $this); + $world->addSound($this->position, new DoorSound()); return true; } } diff --git a/src/block/Vine.php b/src/block/Vine.php index 947013ebd..7706d4f7f 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -141,10 +141,11 @@ class Vine extends Flowable{ } if($changed){ + $world = $this->position->getWorld(); if(count($this->faces) === 0){ - $this->position->getWorld()->useBreakOn($this->position); + $world->useBreakOn($this->position); }else{ - $this->position->getWorld()->setBlock($this->position, $this); + $world->setBlock($this->position, $this); } } } diff --git a/src/block/inventory/BarrelInventory.php b/src/block/inventory/BarrelInventory.php index 7de83bb03..0d17d2a3e 100644 --- a/src/block/inventory/BarrelInventory.php +++ b/src/block/inventory/BarrelInventory.php @@ -48,9 +48,10 @@ class BarrelInventory extends SimpleInventory implements BlockInventory{ protected function animateBlock(bool $isOpen) : void{ $holder = $this->getHolder(); - $block = $holder->getWorld()->getBlock($holder); + $world = $holder->getWorld(); + $block = $world->getBlock($holder); if($block instanceof Barrel){ - $holder->getWorld()->setBlock($holder, $block->setOpen($isOpen)); + $world->setBlock($holder, $block->setOpen($isOpen)); } } } diff --git a/src/block/utils/FallableTrait.php b/src/block/utils/FallableTrait.php index 9275b9d4f..3d7e12214 100644 --- a/src/block/utils/FallableTrait.php +++ b/src/block/utils/FallableTrait.php @@ -46,14 +46,15 @@ trait FallableTrait{ public function onNearbyBlockChange() : void{ $pos = $this->getPosition(); - $down = $pos->getWorld()->getBlock($pos->getSide(Facing::DOWN)); + $world = $pos->getWorld(); + $down = $world->getBlock($pos->getSide(Facing::DOWN)); if($down->canBeReplaced()){ - $pos->getWorld()->setBlock($pos, VanillaBlocks::AIR()); + $world->setBlock($pos, VanillaBlocks::AIR()); $block = $this; if(!($block instanceof Block)) throw new AssumptionFailedError(__TRAIT__ . " should only be used by Blocks"); - $fall = new FallingBlock(Location::fromObject($pos->add(0.5, 0, 0.5), $pos->getWorld()), $block); + $fall = new FallingBlock(Location::fromObject($pos->add(0.5, 0, 0.5), $world), $block); $fall->spawnToAll(); } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 5237cdb4d..6b135c4c4 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -60,15 +60,16 @@ class PreSpawnPacketHandler extends PacketHandler{ public function setUp() : void{ $location = $this->player->getLocation(); + $world = $location->getWorld(); $levelSettings = new LevelSettings(); $levelSettings->seed = -1; $levelSettings->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly $levelSettings->worldGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->server->getGamemode()); - $levelSettings->difficulty = $location->getWorld()->getDifficulty(); - $levelSettings->spawnPosition = BlockPosition::fromVector3($location->getWorld()->getSpawnLocation()); + $levelSettings->difficulty = $world->getDifficulty(); + $levelSettings->spawnPosition = BlockPosition::fromVector3($world->getSpawnLocation()); $levelSettings->hasAchievementsDisabled = true; - $levelSettings->time = $location->getWorld()->getTime(); + $levelSettings->time = $world->getTime(); $levelSettings->eduEditionOffer = 0; $levelSettings->rainLevel = 0; //TODO: implement these properly $levelSettings->lightningLevel = 0; From 42f9336f7acdd1eecd6691ac4aeca6abfbbaa3c6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Jul 2022 21:16:52 +0100 Subject: [PATCH 07/53] Split packet receive timings into decode and handle subcomponents --- src/network/mcpe/NetworkSession.php | 11 +++++++++-- src/timings/Timings.php | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 9fde7ed23..ee965cfa1 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -374,9 +374,8 @@ class NetworkSession{ throw new PacketHandlingException("Unexpected non-serverbound packet"); } - $timings = Timings::getReceiveDataPacketTimings($packet); + $timings = Timings::getDecodeDataPacketTimings($packet); $timings->startTiming(); - try{ $stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext); try{ @@ -388,7 +387,15 @@ class NetworkSession{ $remains = substr($stream->getBuffer(), $stream->getOffset()); $this->logger->debug("Still " . strlen($remains) . " bytes unread in " . $packet->getName() . ": " . bin2hex($remains)); } + }finally{ + $timings->stopTiming(); + } + $timings = Timings::getHandleDataPacketTimings($packet); + $timings->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))){ diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 6f0b38cc7..5d0ece80d 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -119,6 +119,12 @@ abstract class Timings{ public static $tileEntityTypeTimingMap = []; /** @var TimingsHandler[] */ public static $packetReceiveTimingMap = []; + + /** @var TimingsHandler[] */ + private static array $packetDecodeTimingMap = []; + /** @var TimingsHandler[] */ + private static array $packetHandleTimingMap = []; + /** @var TimingsHandler[] */ public static $packetSendTimingMap = []; /** @var TimingsHandler[] */ @@ -229,6 +235,22 @@ abstract class Timings{ return self::$packetReceiveTimingMap[$pid]; } + public static function getDecodeDataPacketTimings(ServerboundPacket $pk) : TimingsHandler{ + $pid = $pk->pid(); + return self::$packetDecodeTimingMap[$pid] ??= new TimingsHandler( + self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Decode - " . $pk->getName() . " [0x" . dechex($pid) . "]", + self::getReceiveDataPacketTimings($pk) + ); + } + + public static function getHandleDataPacketTimings(ServerboundPacket $pk) : TimingsHandler{ + $pid = $pk->pid(); + return self::$packetHandleTimingMap[$pid] ??= new TimingsHandler( + self::INCLUDED_BY_OTHER_TIMINGS_PREFIX . "Handler - " . $pk->getName() . " [0x" . dechex($pid) . "]", + self::getReceiveDataPacketTimings($pk) + ); + } + public static function getSendDataPacketTimings(ClientboundPacket $pk) : TimingsHandler{ $pid = $pk->pid(); if(!isset(self::$packetSendTimingMap[$pid])){ From e90abecf38d9c57635fa0497514bba7e546a2469 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 17 Aug 2022 14:35:56 +0100 Subject: [PATCH 08/53] Rewrite InventoryManager handling of complex mapped inventories this turned out to be necessary when dealing with the ItemStackRequest system. --- src/network/mcpe/ComplexWindowMapEntry.php | 64 ++++++++++ src/network/mcpe/InventoryManager.php | 140 ++++++++++++++++----- src/network/mcpe/convert/TypeConverter.php | 41 +----- 3 files changed, 181 insertions(+), 64 deletions(-) create mode 100644 src/network/mcpe/ComplexWindowMapEntry.php diff --git a/src/network/mcpe/ComplexWindowMapEntry.php b/src/network/mcpe/ComplexWindowMapEntry.php new file mode 100644 index 000000000..c2792297b --- /dev/null +++ b/src/network/mcpe/ComplexWindowMapEntry.php @@ -0,0 +1,64 @@ + + */ + private array $reverseSlotMap = []; + + /** + * @param int[] $slotMap + * @phpstan-param array $slotMap + */ + public function __construct( + private Inventory $inventory, + private array $slotMap + ){ + foreach($slotMap as $slot => $index){ + $this->reverseSlotMap[$index] = $slot; + } + } + + public function getInventory() : Inventory{ return $this->inventory; } + + /** + * @return int[] + * @phpstan-return array + */ + public function getSlotMap() : array{ return $this->slotMap; } + + public function mapNetToCore(int $slot) : ?int{ + return $this->slotMap[$slot] ?? null; + } + + public function mapCoreToNet(int $slot) : ?int{ + return $this->reverseSlotMap[$slot] ?? null; + } +} diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 4b7f870f8..323136086 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -53,6 +53,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction; +use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset; use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes; use pocketmine\network\PacketHandlingException; use pocketmine\player\Player; @@ -61,6 +62,7 @@ use pocketmine\utils\ObjectSet; use function array_map; use function array_search; use function get_class; +use function is_int; use function max; use function spl_object_id; @@ -70,6 +72,17 @@ use function spl_object_id; class InventoryManager{ /** @var Inventory[] */ private array $windowMap = []; + /** + * @var ComplexWindowMapEntry[] + * @phpstan-var array + */ + private array $complexWindows = []; + /** + * @var ComplexWindowMapEntry[] + * @phpstan-var array + */ + private array $complexSlotToWindowMap = []; + private int $lastInventoryNetworkId = ContainerIds::FIRST; /** @@ -96,7 +109,8 @@ class InventoryManager{ $this->add(ContainerIds::INVENTORY, $this->player->getInventory()); $this->add(ContainerIds::OFFHAND, $this->player->getOffHandInventory()); $this->add(ContainerIds::ARMOR, $this->player->getArmorInventory()); - $this->add(ContainerIds::UI, $this->player->getCursorInventory()); + $this->addComplex(UIInventorySlotOffset::CURSOR, $this->player->getCursorInventory()); + $this->addComplex(UIInventorySlotOffset::CRAFTING2X2_INPUT, $this->player->getCraftingGrid()); $this->player->getInventory()->getHeldItemIndexChangeListeners()->add(function() : void{ $this->syncSelectedHotbarSlot(); @@ -113,8 +127,27 @@ class InventoryManager{ return $this->lastInventoryNetworkId; } + /** + * @param int[]|int $slotMap + * @phpstan-param array|int $slotMap + */ + private function addComplex(array|int $slotMap, Inventory $inventory) : void{ + $entry = new ComplexWindowMapEntry($inventory, is_int($slotMap) ? [$slotMap => 0] : $slotMap); + $this->complexWindows[spl_object_id($inventory)] = $entry; + foreach($entry->getSlotMap() as $netSlot => $coreSlot){ + $this->complexSlotToWindowMap[$netSlot] = $entry; + } + } + private function remove(int $id) : void{ - unset($this->windowMap[$id], $this->initiatedSlotChanges[$id]); + $inventory = $this->windowMap[$id]; + $splObjectId = spl_object_id($inventory); + unset($this->windowMap[$id], $this->initiatedSlotChanges[$id], $this->complexWindows[$splObjectId]); + foreach($this->complexSlotToWindowMap as $netSlot => $entry){ + if($entry->getInventory() === $inventory){ + unset($this->complexSlotToWindowMap[$netSlot]); + } + } } public function getWindowId(Inventory $inventory) : ?int{ @@ -125,8 +158,22 @@ class InventoryManager{ return $this->lastInventoryNetworkId; } - public function getWindow(int $windowId) : ?Inventory{ - return $this->windowMap[$windowId] ?? null; + /** + * @phpstan-return array{Inventory, int} + */ + public function locateWindowAndSlot(int $windowId, int $netSlotId) : ?array{ + if($windowId === ContainerIds::UI){ + $entry = $this->complexSlotToWindowMap[$netSlotId] ?? null; + if($entry === null){ + return null; + } + $coreSlotId = $entry->mapNetToCore($netSlotId); + return $coreSlotId !== null ? [$entry->getInventory(), $coreSlotId] : null; + } + if(isset($this->windowMap[$windowId])){ + return [$this->windowMap[$windowId], $netSlotId]; + } + return null; } public function onTransactionStart(InventoryTransaction $tx) : void{ @@ -179,11 +226,30 @@ class InventoryManager{ } } + /** + * @return int[]|null + * @phpstan-return array|null + */ + private function createComplexSlotMapping(Inventory $inventory) : ?array{ + //TODO: make this dynamic so plugins can add mappings for stuff not implemented by PM + return match(true){ + $inventory instanceof AnvilInventory => UIInventorySlotOffset::ANVIL, + $inventory instanceof EnchantInventory => UIInventorySlotOffset::ENCHANTING_TABLE, + $inventory instanceof LoomInventory => UIInventorySlotOffset::LOOM, + $inventory instanceof StonecutterInventory => [UIInventorySlotOffset::STONE_CUTTER_INPUT => StonecutterInventory::SLOT_INPUT], + $inventory instanceof CraftingTableInventory => UIInventorySlotOffset::CRAFTING3X3_INPUT, + default => null, + }; + } + public function onCurrentWindowChange(Inventory $inventory) : void{ $this->onCurrentWindowRemove(); $this->openWindowDeferred(function() use ($inventory) : void{ $windowId = $this->addDynamic($inventory); + if(($slotMap = $this->createComplexSlotMapping($inventory)) !== null){ + $this->addComplex($slotMap, $inventory); + } foreach($this->containerOpenCallbacks as $callback){ $pks = $callback($windowId, $inventory); @@ -280,10 +346,17 @@ class InventoryManager{ } public function syncSlot(Inventory $inventory, int $slot) : void{ - $windowId = $this->getWindowId($inventory); - if($windowId !== null){ + $slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null; + if($slotMap !== null){ + $windowId = ContainerIds::UI; + $netSlot = $slotMap->mapCoreToNet($slot) ?? null; + }else{ + $windowId = $this->getWindowId($inventory); + $netSlot = $slot; + } + if($windowId !== null && $netSlot !== null){ $currentItem = $inventory->getItem($slot); - $clientSideItem = $this->initiatedSlotChanges[$windowId][$slot] ?? null; + $clientSideItem = $this->initiatedSlotChanges[$windowId][$netSlot] ?? null; if($clientSideItem === null || !$clientSideItem->equalsExact($currentItem)){ $itemStackWrapper = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($currentItem)); if($windowId === ContainerIds::OFFHAND){ @@ -294,30 +367,37 @@ class InventoryManager{ //BDS (Bedrock Dedicated Server) also seems to work this way. $this->session->sendDataPacket(InventoryContentPacket::create($windowId, [$itemStackWrapper])); }else{ - $this->session->sendDataPacket(InventorySlotPacket::create($windowId, $slot, $itemStackWrapper)); + $this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper)); } } - unset($this->initiatedSlotChanges[$windowId][$slot]); + unset($this->initiatedSlotChanges[$windowId][$netSlot]); } } public function syncContents(Inventory $inventory) : void{ - $windowId = $this->getWindowId($inventory); + $slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null; + if($slotMap !== null){ + $windowId = ContainerIds::UI; + }else{ + $windowId = $this->getWindowId($inventory); + } + $typeConverter = TypeConverter::getInstance(); if($windowId !== null){ - unset($this->initiatedSlotChanges[$windowId]); - $typeConverter = TypeConverter::getInstance(); - if($windowId === ContainerIds::UI){ - //TODO: HACK! - //Since 1.13, cursor is now part of a larger "UI inventory", and sending contents for this larger inventory does - //not work the way it's intended to. Even if it did, it would be necessary to send all 51 slots just to update - //this one, which is just not worth it. - //This workaround isn't great, but it's at least simple. - $this->session->sendDataPacket(InventorySlotPacket::create( - $windowId, - 0, - ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($inventory->getItem(0))) - )); + if($slotMap !== null){ + foreach($inventory->getContents(true) as $slotId => $item){ + $packetSlot = $slotMap->mapCoreToNet($slotId) ?? null; + if($packetSlot === null){ + continue; + } + unset($this->initiatedSlotChanges[$windowId][$packetSlot]); + $this->session->sendDataPacket(InventorySlotPacket::create( + $windowId, + $packetSlot, + ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($inventory->getItem($slotId))) + )); + } }else{ + unset($this->initiatedSlotChanges[$windowId]); $this->session->sendDataPacket(InventoryContentPacket::create($windowId, array_map(function(Item $itemStack) use ($typeConverter) : ItemStackWrapper{ return ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($itemStack)); }, $inventory->getContents(true)))); @@ -329,16 +409,20 @@ class InventoryManager{ foreach($this->windowMap as $inventory){ $this->syncContents($inventory); } + foreach($this->complexWindows as $entry){ + $this->syncContents($entry->getInventory()); + } } public function syncMismatchedPredictedSlotChanges() : void{ foreach($this->initiatedSlotChanges as $windowId => $slots){ - if(!isset($this->windowMap[$windowId])){ - continue; - } - $inventory = $this->windowMap[$windowId]; + foreach($slots as $netSlot => $expectedItem){ + $located = $this->locateWindowAndSlot($windowId, $netSlot); + if($located === null){ + continue; + } + [$inventory, $slot] = $located; - foreach($slots as $slot => $expectedItem){ if(!$inventory->slotExists($slot)){ continue; //TODO: size desync ??? } diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php index 2a74c9f07..cf231af42 100644 --- a/src/network/mcpe/convert/TypeConverter.php +++ b/src/network/mcpe/convert/TypeConverter.php @@ -24,11 +24,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\convert; use pocketmine\block\BlockLegacyIds; -use pocketmine\block\inventory\AnvilInventory; -use pocketmine\block\inventory\CraftingTableInventory; -use pocketmine\block\inventory\EnchantInventory; -use pocketmine\block\inventory\LoomInventory; -use pocketmine\block\inventory\StonecutterInventory; use pocketmine\inventory\transaction\action\CreateItemAction; use pocketmine\inventory\transaction\action\DestroyItemAction; use pocketmine\inventory\transaction\action\DropItemAction; @@ -273,38 +268,12 @@ class TypeConverter{ } switch($action->sourceType){ case NetworkInventoryAction::SOURCE_CONTAINER: - $window = null; - if($action->windowId === ContainerIds::UI && $action->inventorySlot > 0){ - if($action->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ - return null; //useless noise - } - $pSlot = $action->inventorySlot; - - $slot = UIInventorySlotOffset::CRAFTING2X2_INPUT[$pSlot] ?? null; - if($slot !== null){ - $window = $player->getCraftingGrid(); - }elseif(($current = $player->getCurrentWindow()) !== null){ - $slotMap = match(true){ - $current instanceof AnvilInventory => UIInventorySlotOffset::ANVIL, - $current instanceof EnchantInventory => UIInventorySlotOffset::ENCHANTING_TABLE, - $current instanceof LoomInventory => UIInventorySlotOffset::LOOM, - $current instanceof StonecutterInventory => [UIInventorySlotOffset::STONE_CUTTER_INPUT => StonecutterInventory::SLOT_INPUT], - $current instanceof CraftingTableInventory => UIInventorySlotOffset::CRAFTING3X3_INPUT, - default => null - }; - if($slotMap !== null){ - $window = $current; - $slot = $slotMap[$pSlot] ?? null; - } - } - if($slot === null){ - throw new TypeConversionException("Unmatched UI inventory slot offset $pSlot"); - } - }else{ - $window = $inventoryManager->getWindow($action->windowId); - $slot = $action->inventorySlot; + if($action->windowId === ContainerIds::UI && $action->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ + return null; //useless noise } - if($window !== null){ + $located = $inventoryManager->locateWindowAndSlot($action->windowId, $action->inventorySlot); + if($located !== null){ + [$window, $slot] = $located; return new SlotChangeAction($window, $slot, $old, $new); } From 792c1b62b71fbfdc2bed18b2d374432668a3c733 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 18 Aug 2022 17:03:39 +0100 Subject: [PATCH 09/53] Introduce and use TransactionBuilder for inventory evacuations --- .../transaction/TransactionBuilder.php | 61 +++++++++++++++++++ src/player/Player.php | 22 +++---- 2 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 src/inventory/transaction/TransactionBuilder.php diff --git a/src/inventory/transaction/TransactionBuilder.php b/src/inventory/transaction/TransactionBuilder.php new file mode 100644 index 000000000..f56b2aaa1 --- /dev/null +++ b/src/inventory/transaction/TransactionBuilder.php @@ -0,0 +1,61 @@ +extraActions[spl_object_id($action)] = $action; + } + + public function getInventory(Inventory $inventory) : TransactionBuilderInventory{ + $id = spl_object_id($inventory); + return $this->inventories[$id] ??= new TransactionBuilderInventory($inventory); + } + + /** + * @return InventoryAction[] + */ + public function generateActions() : array{ + $actions = $this->extraActions; + + foreach($this->inventories as $inventory){ + foreach($inventory->generateActions() as $action){ + $actions[spl_object_id($action)] = $action; + } + } + + return $actions; + } +} diff --git a/src/player/Player.php b/src/player/Player.php index 86e18a6c6..30d82a538 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -85,7 +85,7 @@ use pocketmine\inventory\PlayerCursorInventory; use pocketmine\inventory\TemporaryInventory; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\InventoryTransaction; -use pocketmine\inventory\transaction\TransactionBuilderInventory; +use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionCancelledException; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\ConsumableItem; @@ -2436,29 +2436,23 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $inventories[] = $this->currentWindow; } - $transaction = new InventoryTransaction($this); - $mainInventoryTransactionBuilder = new TransactionBuilderInventory($this->inventory); + $builder = new TransactionBuilder(); foreach($inventories as $inventory){ $contents = $inventory->getContents(); if(count($contents) > 0){ - $drops = $mainInventoryTransactionBuilder->addItem(...$contents); + $drops = $builder->getInventory($this->inventory)->addItem(...$contents); foreach($drops as $drop){ - $transaction->addAction(new DropItemAction($drop)); + $builder->addAction(new DropItemAction($drop)); } - $clearedInventoryTransactionBuilder = new TransactionBuilderInventory($inventory); - $clearedInventoryTransactionBuilder->clearAll(); - foreach($clearedInventoryTransactionBuilder->generateActions() as $action){ - $transaction->addAction($action); - } + $builder->getInventory($inventory)->clearAll(); } } - foreach($mainInventoryTransactionBuilder->generateActions() as $action){ - $transaction->addAction($action); - } - if(count($transaction->getActions()) !== 0){ + $actions = $builder->generateActions(); + if(count($actions) !== 0){ + $transaction = new InventoryTransaction($this, $actions); try{ $transaction->execute(); $this->logger->debug("Successfully evacuated items from temporary inventories"); From a25cb3741a666ba740f44161cfdcfb02edd9866d Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 <66992287+ShockedPlot7560@users.noreply.github.com> Date: Wed, 31 Aug 2022 03:18:18 +0200 Subject: [PATCH 10/53] Limit valid supporting blocks of dead bush (#5055) --- src/block/DeadBush.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/block/DeadBush.php b/src/block/DeadBush.php index 09754b07d..72cbc0de5 100644 --- a/src/block/DeadBush.php +++ b/src/block/DeadBush.php @@ -34,7 +34,7 @@ use function mt_rand; class DeadBush extends Flowable{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(!$this->getSide(Facing::DOWN)->isTransparent()){ + if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -42,7 +42,7 @@ class DeadBush extends Flowable{ } public function onNearbyBlockChange() : void{ - if($this->getSide(Facing::DOWN)->isTransparent()){ + if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){ $this->position->getWorld()->useBreakOn($this->position); } } @@ -64,4 +64,14 @@ class DeadBush extends Flowable{ public function getFlammability() : int{ return 100; } + + private function canBeSupportedBy(Block $block) : bool{ + $blockId = $block->getId(); + return $blockId === BlockLegacyIds::SAND + || $blockId === BlockLegacyIds::PODZOL + || $blockId === BlockLegacyIds::MYCELIUM + || $blockId === BlockLegacyIds::DIRT + || $blockId === BlockLegacyIds::HARDENED_CLAY + || $blockId === BlockLegacyIds::STAINED_HARDENED_CLAY; + } } From 65ec318c3096da089afc37777749f09bb48f8ca8 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Wed, 31 Aug 2022 18:43:30 +0100 Subject: [PATCH 11/53] PluginManager: Ensure dependents are disabled before dependencies in disablePlugins() (#5227) this could later be expanded to disablePlugin() to make this disable order mandatory, to provide certainty for plugin devs. Alternative solutions to this include disabling plugins in the opposite order that they were enabled in, but this doesn't allow for random plugin disables. This way seemed to make sense. --- src/plugin/PluginManager.php | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index acffae53e..4fdd7ee76 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -75,6 +75,9 @@ class PluginManager{ /** @var Plugin[] */ protected $enabledPlugins = []; + /** @var array> */ + private array $pluginDependents = []; + private bool $loadPluginsGuard = false; /** @@ -453,6 +456,15 @@ class PluginManager{ if($plugin->isEnabled()){ //the plugin may have disabled itself during onEnable() $this->enabledPlugins[$plugin->getDescription()->getName()] = $plugin; + foreach($plugin->getDescription()->getDepend() as $dependency){ + $this->pluginDependents[$dependency][$plugin->getDescription()->getName()] = true; + } + foreach($plugin->getDescription()->getSoftDepend() as $dependency){ + if(isset($this->plugins[$dependency])){ + $this->pluginDependents[$dependency][$plugin->getDescription()->getName()] = true; + } + } + (new PluginEnableEvent($plugin))->call(); return true; @@ -472,8 +484,19 @@ class PluginManager{ } public function disablePlugins() : void{ - foreach($this->getPlugins() as $plugin){ - $this->disablePlugin($plugin); + while(count($this->enabledPlugins) > 0){ + foreach($this->enabledPlugins as $plugin){ + if(!$plugin->isEnabled()){ + continue; //in case a plugin disabled another plugin + } + $name = $plugin->getDescription()->getName(); + if(isset($this->pluginDependents[$name]) && count($this->pluginDependents[$name]) > 0){ + $this->server->getLogger()->debug("Deferring disable of plugin $name due to dependent plugins still enabled: " . implode(", ", array_keys($this->pluginDependents[$name]))); + continue; + } + + $this->disablePlugin($plugin); + } } } @@ -483,6 +506,12 @@ class PluginManager{ (new PluginDisableEvent($plugin))->call(); unset($this->enabledPlugins[$plugin->getDescription()->getName()]); + foreach(Utils::stringifyKeys($this->pluginDependents) as $dependency => $dependentList){ + unset($this->pluginDependents[$dependency][$plugin->getDescription()->getName()]); + if(count($this->pluginDependents[$dependency]) === 0){ + unset($this->pluginDependents[$dependency]); + } + } $plugin->onEnableStateChange(false); $plugin->getScheduler()->shutdown(); From 14d3e6c7d5f98b13ddbc39fab55a0512f9c9c262 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 1 Sep 2022 23:43:13 +0100 Subject: [PATCH 12/53] Allow disabling the console reader via pocketmine.yml Useful to save resources on headless servers where the console is never used (e.g. hosted server, Docker, etc.) --- resources/pocketmine.yml | 3 +++ src/Server.php | 28 +++++++++++++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/resources/pocketmine.yml b/resources/pocketmine.yml index f922cb225..4ad8ea9c0 100644 --- a/resources/pocketmine.yml +++ b/resources/pocketmine.yml @@ -168,6 +168,9 @@ timings: host: timings.pmmp.io console: + #Whether to accept commands via the console. If disabled, anything typed on the console will be ignored. + #Useful to save resources on headless servers where the console is never used (e.g. hosted server, Docker, etc.) + enable-input: true #Choose whether to enable server stats reporting on the console title. #NOTE: The title ticker will be disabled regardless if console colours are not enabled. title-tick: true diff --git a/src/Server.php b/src/Server.php index 3128a7c39..25b689093 100644 --- a/src/Server.php +++ b/src/Server.php @@ -225,7 +225,7 @@ class Server{ private MemoryManager $memoryManager; - private ConsoleReaderThread $console; + private ?ConsoleReaderThread $console = null; private SimpleCommandMap $commandMap; @@ -1048,17 +1048,19 @@ class Server{ $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender); - $consoleNotifier = new SleeperNotifier(); - $commandBuffer = new \Threaded(); - $this->console = new ConsoleReaderThread($commandBuffer, $consoleNotifier); - $this->tickSleeper->addNotifier($consoleNotifier, function() use ($commandBuffer, $consoleSender) : void{ - Timings::$serverCommand->startTiming(); - while(($line = $commandBuffer->shift()) !== null){ - $this->dispatchCommand($consoleSender, (string) $line); - } - Timings::$serverCommand->stopTiming(); - }); - $this->console->start(PTHREADS_INHERIT_NONE); + if($this->configGroup->getPropertyBool("console.enable-input", true)){ + $consoleNotifier = new SleeperNotifier(); + $commandBuffer = new \Threaded(); + $this->console = new ConsoleReaderThread($commandBuffer, $consoleNotifier); + $this->tickSleeper->addNotifier($consoleNotifier, function() use ($commandBuffer, $consoleSender) : void{ + Timings::$serverCommand->startTiming(); + while(($line = $commandBuffer->shift()) !== null){ + $this->dispatchCommand($consoleSender, (string) $line); + } + Timings::$serverCommand->stopTiming(); + }); + $this->console->start(PTHREADS_INHERIT_NONE); + } $this->tickProcessor(); $this->forceShutdown(); @@ -1511,7 +1513,7 @@ class Server{ $this->configGroup->save(); } - if(isset($this->console)){ + if($this->console !== null){ $this->getLogger()->debug("Closing console"); $this->console->quit(); } From 2585160ca2c4df5758b8b980331307402ff9f0fb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 2 Sep 2022 00:25:31 +0100 Subject: [PATCH 13/53] ConsoleReaderChildProcess: Commit suicide if the parent process dies and doesn't clean up This happens if the main server process was forcibly killed, e.g. by the kill command on Linux, or taskkill/TaskManager on Windows. Previously, the process would stick around as a zombie, which messed up terminals in some cases (e.g. git bash), though even having zombies with no side effects is bad enough. --- src/console/ConsoleReaderChildProcess.php | 42 +++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/console/ConsoleReaderChildProcess.php b/src/console/ConsoleReaderChildProcess.php index 5bf2ff71f..2d4e3fc56 100644 --- a/src/console/ConsoleReaderChildProcess.php +++ b/src/console/ConsoleReaderChildProcess.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace pocketmine\console; +use pocketmine\utils\Process; use function cli_set_process_title; use function count; use function dirname; use function feof; use function fwrite; use function stream_socket_client; +use const PTHREADS_INHERIT_NONE; require dirname(__DIR__, 2) . '/vendor/autoload.php'; @@ -43,9 +45,40 @@ $socket = stream_socket_client($argv[1], $errCode, $errMessage, 15.0); if($socket === false){ throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage"); } -$consoleReader = new ConsoleReader(); + +$channel = new \Threaded(); +$thread = new class($channel) extends \Thread{ + public function __construct( + private \Threaded $channel, + ){} + + public function run(){ + require dirname(__DIR__, 2) . '/vendor/autoload.php'; + + $channel = $this->channel; + $reader = new ConsoleReader(); + while(true){ // @phpstan-ignore-line + $line = $reader->readLine(); + if($line !== null){ + $channel->synchronized(function() use ($channel, $line) : void{ + $channel[] = $line; + $channel->notify(); + }); + } + } + } +}; + +$thread->start(PTHREADS_INHERIT_NONE); while(!feof($socket)){ - $line = $consoleReader->readLine(); + $line = $channel->synchronized(function() use ($channel) : ?string{ + if(count($channel) === 0){ + $channel->wait(1_000_000); + } + /** @var string|null $line */ + $line = $channel->shift(); + return $line; + }); if(@fwrite($socket, ($line ?? "") . "\n") === false){ //Always send even if there's no line, to check if the parent is alive //If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return @@ -53,3 +86,8 @@ while(!feof($socket)){ break; } } + +//For simplicity's sake, we don't bother with a graceful shutdown here. +//The parent process would normally forcibly terminate the child process anyway, so we only reach this point if the +//parent process was terminated forcibly and didn't clean up after itself. +Process::kill(Process::pid(), false); From b3f03d7ae645de67a54b7300c09b94eeca16298e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 2 Sep 2022 00:58:49 +0100 Subject: [PATCH 14/53] Poll console on the main thread, instead of using a separate thread There's no need to use an extra thread for this, since there's no concern of a socket getting stuck in a blocking read. This is one less thing that can go wrong because of pthreads. --- src/Server.php | 24 ++-- ...hp => ConsoleReaderChildProcessDaemon.php} | 115 +++++++++--------- 2 files changed, 68 insertions(+), 71 deletions(-) rename src/console/{ConsoleReaderThread.php => ConsoleReaderChildProcessDaemon.php} (50%) diff --git a/src/Server.php b/src/Server.php index 25b689093..3c289f4fa 100644 --- a/src/Server.php +++ b/src/Server.php @@ -31,7 +31,7 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\command\SimpleCommandMap; use pocketmine\console\ConsoleCommandSender; -use pocketmine\console\ConsoleReaderThread; +use pocketmine\console\ConsoleReaderChildProcessDaemon; use pocketmine\crafting\CraftingManager; use pocketmine\crafting\CraftingManagerFromDataHelper; use pocketmine\crash\CrashDump; @@ -225,7 +225,8 @@ class Server{ private MemoryManager $memoryManager; - private ?ConsoleReaderThread $console = null; + private ?ConsoleReaderChildProcessDaemon $console = null; + private ?ConsoleCommandSender $consoleSender = null; private SimpleCommandMap $commandMap; @@ -1049,17 +1050,7 @@ class Server{ $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender); if($this->configGroup->getPropertyBool("console.enable-input", true)){ - $consoleNotifier = new SleeperNotifier(); - $commandBuffer = new \Threaded(); - $this->console = new ConsoleReaderThread($commandBuffer, $consoleNotifier); - $this->tickSleeper->addNotifier($consoleNotifier, function() use ($commandBuffer, $consoleSender) : void{ - Timings::$serverCommand->startTiming(); - while(($line = $commandBuffer->shift()) !== null){ - $this->dispatchCommand($consoleSender, (string) $line); - } - Timings::$serverCommand->stopTiming(); - }); - $this->console->start(PTHREADS_INHERIT_NONE); + $this->console = new ConsoleReaderChildProcessDaemon($this->logger); } $this->tickProcessor(); @@ -1855,6 +1846,13 @@ class Server{ $this->getMemoryManager()->check(); + if($this->console !== null){ + while(($line = $this->console->readLine()) !== null){ + $this->consoleSender ??= new ConsoleCommandSender($this, $this->language); + $this->dispatchCommand($this->consoleSender, $line); + } + } + Timings::$serverTick->stopTiming(); $now = microtime(true); diff --git a/src/console/ConsoleReaderThread.php b/src/console/ConsoleReaderChildProcessDaemon.php similarity index 50% rename from src/console/ConsoleReaderThread.php rename to src/console/ConsoleReaderChildProcessDaemon.php index eac19ef84..a0c689d37 100644 --- a/src/console/ConsoleReaderThread.php +++ b/src/console/ConsoleReaderChildProcessDaemon.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace pocketmine\console; -use pocketmine\snooze\SleeperNotifier; -use pocketmine\thread\Thread; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use Webmozart\PathUtil\Path; @@ -45,36 +43,35 @@ use function trim; use const PHP_BINARY; use const STREAM_SHUT_RDWR; -final class ConsoleReaderThread extends Thread{ +/** + * This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes + * properly - stdin native triggers stream_select() when a key is pressed, causing it to get stuck in fgets() + * waiting for a line that might never come (and Windows doesn't support character-based reading either), and + * pipes just constantly trigger stream_select() instead of only when data is returned, rendering it useless. + * + * This results in whichever process reads stdin getting stuck on shutdown, which previously forced us to kill + * the entire server process to make it go away. + * + * To get around this problem, we delegate the responsibility of reading stdin to a subprocess, which we can + * then brutally murder when the server shuts down, without killing the entire server process. + * Thankfully, stream_select() actually works properly on sockets, so we can use them for inter-process + * communication. + */ +final class ConsoleReaderChildProcessDaemon{ + private \PrefixedLogger $logger; + /** @var resource */ + private $subprocess; + /** @var resource */ + private $socket; + public function __construct( - private \Threaded $buffer, - private ?SleeperNotifier $notifier = null - ){} - - protected function onRun() : void{ - $buffer = $this->buffer; - $notifier = $this->notifier; - - while(!$this->isKilled){ - $this->runSubprocess($buffer, $notifier); - } + \Logger $logger + ){ + $this->logger = new \PrefixedLogger($logger, "Console Reader Daemon"); + $this->prepareSubprocess(); } - /** - * This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes - * properly - stdin native triggers stream_select() when a key is pressed, causing it to get stuck in fgets() - * waiting for a line that might never come (and Windows doesn't support character-based reading either), and - * pipes just constantly trigger stream_select() instead of only when data is returned, rendering it useless. - * - * This results in whichever process reads stdin getting stuck on shutdown, which previously forced us to kill - * the entire server process to make it go away. - * - * To get around this problem, we delegate the responsibility of reading stdin to a subprocess, which we can - * then brutally murder when the server shuts down, without killing the entire server process. - * Thankfully, stream_select() actually works properly on sockets, so we can use them for inter-process - * communication. - */ - private function runSubprocess(\Threaded $buffer, ?SleeperNotifier $notifier) : void{ + private function prepareSubprocess() : void{ $server = stream_socket_server("tcp://127.0.0.1:0"); if($server === false){ throw new \RuntimeException("Failed to open console reader socket server"); @@ -96,41 +93,43 @@ final class ConsoleReaderThread extends Thread{ throw new AssumptionFailedError("stream_socket_accept() returned false"); } stream_socket_shutdown($server, STREAM_SHUT_RDWR); - while(!$this->isKilled){ - $r = [$client]; - $w = null; - $e = null; - if(stream_select($r, $w, $e, 0, 200000) === 1){ - $command = fgets($client); - if($command === false){ - //subprocess died for some reason; this could be someone killed it manually from outside (e.g. - //mistyped PID) or it might be a ctrl+c signal to this process that the child is handling - //differently (different signal handlers). - //since we have no way to know the difference, we just kill the sub and start a new one. - break; - } - $command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); - $command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); - if($command === ""){ - continue; - } - $buffer[] = $command; - if($notifier !== null){ - $notifier->wakeupSleeper(); - } - } - } + $this->subprocess = $sub; + $this->socket = $client; + } + private function shutdownSubprocess() : void{ //we have no way to signal to the subprocess to shut down gracefully; besides, Windows sucks, and the subprocess //gets stuck in a blocking fgets() read because stream_select() is a hunk of junk (hence the separate process in //the first place). - proc_terminate($sub); - proc_close($sub); - stream_socket_shutdown($client, STREAM_SHUT_RDWR); + proc_terminate($this->subprocess); + proc_close($this->subprocess); + stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); } - public function getThreadName() : string{ - return "Console"; + public function readLine() : ?string{ + $r = [$this->socket]; + $w = null; + $e = null; + if(stream_select($r, $w, $e, 0, 0) === 1){ + $command = fgets($this->socket); + if($command === false){ + $this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)"); + $this->shutdownSubprocess(); + $this->prepareSubprocess(); + return null; + } + + $command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); + $command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); + + return $command !== "" ? $command : null; + } + + return null; + } + + public function quit() : void{ + $this->shutdownSubprocess(); } } From ffb3af3e0dae4d323952d726cad8be5e72b165fd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 2 Sep 2022 00:59:58 +0100 Subject: [PATCH 15/53] fix CS --- src/Server.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Server.php b/src/Server.php index 3c289f4fa..153789aa3 100644 --- a/src/Server.php +++ b/src/Server.php @@ -88,7 +88,6 @@ use pocketmine\promise\PromiseResolver; use pocketmine\resourcepacks\ResourcePackManager; use pocketmine\scheduler\AsyncPool; use pocketmine\snooze\SleeperHandler; -use pocketmine\snooze\SleeperNotifier; use pocketmine\stats\SendUsageTask; use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; @@ -165,7 +164,6 @@ use function zlib_encode; use const DIRECTORY_SEPARATOR; use const PHP_EOL; use const PHP_INT_MAX; -use const PTHREADS_INHERIT_NONE; use const ZLIB_ENCODING_GZIP; /** From 9f97654f6f0cd67032168aaa76f3ae51d9d8373f Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:26:37 -0500 Subject: [PATCH 16/53] Update InstantDamage and Regeneration values (#5279) --- src/entity/effect/InstantDamageEffect.php | 2 +- src/entity/effect/RegenerationEffect.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/effect/InstantDamageEffect.php b/src/entity/effect/InstantDamageEffect.php index 9461fd979..28d731079 100644 --- a/src/entity/effect/InstantDamageEffect.php +++ b/src/entity/effect/InstantDamageEffect.php @@ -33,7 +33,7 @@ class InstantDamageEffect extends InstantEffect{ public function applyEffect(Living $entity, EffectInstance $instance, float $potency = 1.0, ?Entity $source = null) : void{ //TODO: add particles (witch spell) - $damage = (4 << $instance->getAmplifier()) * $potency; + $damage = (6 << $instance->getAmplifier()) * $potency; if($source !== null){ $sourceOwner = $source->getOwningEntity(); if($sourceOwner !== null){ diff --git a/src/entity/effect/RegenerationEffect.php b/src/entity/effect/RegenerationEffect.php index 28ba2b18b..f06ce67db 100644 --- a/src/entity/effect/RegenerationEffect.php +++ b/src/entity/effect/RegenerationEffect.php @@ -30,7 +30,7 @@ use pocketmine\event\entity\EntityRegainHealthEvent; class RegenerationEffect extends Effect{ public function canTick(EffectInstance $instance) : bool{ - if(($interval = (40 >> $instance->getAmplifier())) > 0){ + if(($interval = (50 >> $instance->getAmplifier())) > 0){ return ($instance->getDuration() % $interval) === 0; } return true; From e4fc523251c8b32f9669c4745aa838ed7a45b4e7 Mon Sep 17 00:00:00 2001 From: famima65536 Date: Fri, 23 Sep 2022 19:37:08 +0900 Subject: [PATCH 17/53] Introduce Player::sendToastNotification() (#5102) --- src/network/mcpe/NetworkSession.php | 5 +++++ src/player/Player.php | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 156f65978..3b868e0f5 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -88,6 +88,7 @@ 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; use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\network\mcpe\protocol\types\command\CommandData; @@ -1100,6 +1101,10 @@ class NetworkSession{ $this->sendDataPacket(EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER)); } + public function onToastNotification(string $title, string $body) : void{ + $this->sendDataPacket(ToastRequestPacket::create($title, $body)); + } + public function tick() : void{ if($this->info === null){ if(time() >= $this->connectTime + 10){ diff --git a/src/player/Player.php b/src/player/Player.php index 139dc5add..8a6a10fea 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1986,6 +1986,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->getNetworkSession()->onTip($message); } + /** + * Sends a toast message to the player, or queue to send it if a toast message is already shown. + */ + public function sendToastNotification(string $title, string $body) : void{ + $this->getNetworkSession()->onToastNotification($title, $body); + } + /** * Sends a Form to the player, or queue to send it if a form is already open. * From 83a136a176df9a09f85064370b993da4060c7dd4 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Sat, 24 Sep 2022 07:55:24 -0500 Subject: [PATCH 18/53] EntityFactory: Avoid code duplication on validation of creation functions (#5294) --- src/entity/EntityFactory.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index cd1f96a5e..fb3a1f33d 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -175,20 +175,6 @@ final class EntityFactory{ }, ['Human']); } - /** - * @phpstan-param \Closure(World, CompoundTag) : Entity $creationFunc - */ - private static function validateCreationFunc(\Closure $creationFunc) : void{ - $sig = new CallbackType( - new ReturnType(Entity::class), - new ParameterType("world", World::class), - new ParameterType("nbt", CompoundTag::class) - ); - if(!$sig->isSatisfiedBy($creationFunc)){ - throw new \TypeError("Declaration of callable `" . CallbackType::createFromCallable($creationFunc) . "` must be compatible with `" . $sig . "`"); - } - } - /** * Registers an entity type into the index. * @@ -207,7 +193,11 @@ final class EntityFactory{ throw new \InvalidArgumentException("At least one save name must be provided"); } Utils::testValidInstance($className, Entity::class); - self::validateCreationFunc($creationFunc); + Utils::validateCallableSignature(new CallbackType( + new ReturnType(Entity::class), + new ParameterType("world", World::class), + new ParameterType("nbt", CompoundTag::class) + ), $creationFunc); foreach($saveNames as $name){ $this->creationFuncs[$name] = $creationFunc; From 83e5b0adb6fa0dddec377182bb1c7945ac8f7820 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Tue, 27 Sep 2022 20:58:33 +0100 Subject: [PATCH 19/53] ConsoleCommandSender is no longer responsible for forwarding broadcast messages to the logger (#5311) This is a step towards implementing #2543. --- src/Server.php | 9 +-- src/command/Command.php | 3 +- src/utils/BroadcastLoggerForwarder.php | 79 ++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 src/utils/BroadcastLoggerForwarder.php diff --git a/src/Server.php b/src/Server.php index 294fe3c88..7655bb1d8 100644 --- a/src/Server.php +++ b/src/Server.php @@ -93,6 +93,7 @@ use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; use pocketmine\updater\UpdateChecker; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\Config; use pocketmine\utils\Filesystem; use pocketmine\utils\Internet; @@ -1044,11 +1045,11 @@ class Server{ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET))); $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3))))); - //TODO: move console parts to a separate component - $consoleSender = new ConsoleCommandSender($this, $this->language); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender); + $forwarder = new BroadcastLoggerForwarder($this, $this->logger, $this->language); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); + //TODO: move console parts to a separate component if($this->configGroup->getPropertyBool("console.enable-input", true)){ $this->console = new ConsoleReaderChildProcessDaemon($this->logger); } diff --git a/src/command/Command.php b/src/command/Command.php index a85184777..f56ebc504 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -34,6 +34,7 @@ use pocketmine\permission\PermissionManager; use pocketmine\Server; use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; +use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\TextFormat; use function explode; use function str_replace; @@ -232,7 +233,7 @@ abstract class Command{ } foreach($users as $user){ - if($user instanceof ConsoleCommandSender){ + if($user instanceof BroadcastLoggerForwarder){ $user->sendMessage($result); }elseif($user !== $source){ $user->sendMessage($colored); diff --git a/src/utils/BroadcastLoggerForwarder.php b/src/utils/BroadcastLoggerForwarder.php new file mode 100644 index 000000000..a015615b5 --- /dev/null +++ b/src/utils/BroadcastLoggerForwarder.php @@ -0,0 +1,79 @@ +perm = new PermissibleBase([]); + } + + public function getLanguage() : Language{ + return $this->language; + } + + public function sendMessage(Translatable|string $message) : void{ + if($message instanceof Translatable){ + $this->logger->info($this->language->translate($message)); + }else{ + $this->logger->info($message); + } + } + + public function getServer() : Server{ + return $this->server; + } + + public function getName() : string{ + return "Broadcast Logger Forwarder"; + } + + public function getScreenLineHeight() : int{ + return PHP_INT_MAX; + } + + public function setScreenLineHeight(?int $height) : void{ + //NOOP + } +} From cfb0cad7e07c1e222636cb4d089e0888f3d3b7e6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 27 Sep 2022 21:02:49 +0100 Subject: [PATCH 20/53] Console commands now write to stdout directly, instead of being fed through the logger this has a number of implications: - Console command outputs are now (obviously) not logged. This is consistent with every other type of command sender, be it RCON, players, or anything else. - The assumption that the console command sender must be able to see the logger output is now broken, since the command sender can receive output separately from the logs. In the future, it might be desirable to send the console command output to stderr instead of stdout, so that stdout can be silenced while still allowing commands to be used. closes #2543 --- src/command/Command.php | 3 +-- src/console/ConsoleCommandSender.php | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command/Command.php b/src/command/Command.php index f56ebc504..12a1b8e38 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -27,7 +27,6 @@ declare(strict_types=1); namespace pocketmine\command; use pocketmine\command\utils\CommandException; -use pocketmine\console\ConsoleCommandSender; use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\Translatable; use pocketmine\permission\PermissionManager; @@ -228,7 +227,7 @@ abstract class Command{ $result = KnownTranslationFactory::chat_type_admin($source->getName(), $message); $colored = $result->prefix(TextFormat::GRAY . TextFormat::ITALIC); - if($sendToSource && !($source instanceof ConsoleCommandSender)){ + if($sendToSource){ $source->sendMessage($message); } diff --git a/src/console/ConsoleCommandSender.php b/src/console/ConsoleCommandSender.php index 30035ac3e..9b4f4191e 100644 --- a/src/console/ConsoleCommandSender.php +++ b/src/console/ConsoleCommandSender.php @@ -30,6 +30,8 @@ use pocketmine\permission\DefaultPermissions; use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\Server; +use pocketmine\utils\Terminal; +use pocketmine\utils\TextFormat; use function explode; use function trim; use const PHP_INT_MAX; @@ -59,13 +61,12 @@ class ConsoleCommandSender implements CommandSender{ } public function sendMessage(Translatable|string $message) : void{ - $server = $this->getServer(); if($message instanceof Translatable){ $message = $this->getLanguage()->translate($message); } foreach(explode("\n", trim($message)) as $line){ - $server->getLogger()->info($line); + Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::WHITE . $line); } } From b87e4d8bd3482876929df885ee06ef2235999eaf Mon Sep 17 00:00:00 2001 From: Dylan T Date: Wed, 28 Sep 2022 16:13:11 +0100 Subject: [PATCH 21/53] Introduce and use TextFormat::addBase() (#5268) This function adds "base" format to a string. The given formats are inserted directly after any RESET code in the sequence. An example of where this is needed is in the logger. Without this change, the following code: $logger->notice("I'm a " . TextFormat::RED . "special" . TextFormat::RESET . " cookie"); causes the "cookie" part of the message to show as grey, instead of the expected aqua for NOTICE level messages. There are also many workarounds for this problem throughout the server, mostly in command outputs, being forced to use WHITE instead of RESET to avoid breaking the logger output. --- src/Server.php | 2 +- .../defaults/GarbageCollectorCommand.php | 2 +- src/command/defaults/HelpCommand.php | 10 ++++---- src/command/defaults/MeCommand.php | 2 +- src/command/defaults/PluginsCommand.php | 2 +- src/command/defaults/StatusCommand.php | 2 +- src/command/defaults/VersionCommand.php | 19 +++++++------- src/console/ConsoleCommandSender.php | 2 +- src/utils/MainLogger.php | 2 +- src/utils/TextFormat.php | 25 +++++++++++++++++++ 10 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/Server.php b/src/Server.php index 7655bb1d8..cccbdf60b 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1712,7 +1712,7 @@ class Server{ $session = $player->getNetworkSession(); $position = $player->getPosition(); $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_player_logIn( - TextFormat::AQUA . $player->getName() . TextFormat::WHITE, + TextFormat::AQUA . $player->getName() . TextFormat::RESET, $session->getIp(), (string) $session->getPort(), (string) $player->getId(), diff --git a/src/command/defaults/GarbageCollectorCommand.php b/src/command/defaults/GarbageCollectorCommand.php index 69875c7d5..cdfe2b883 100644 --- a/src/command/defaults/GarbageCollectorCommand.php +++ b/src/command/defaults/GarbageCollectorCommand.php @@ -63,7 +63,7 @@ class GarbageCollectorCommand extends VanillaCommand{ $cyclesCollected = $sender->getServer()->getMemoryManager()->triggerGarbageCollector(); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_header()->format(TextFormat::GREEN . "---- " . TextFormat::WHITE, TextFormat::GREEN . " ----" . TextFormat::WHITE)); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_header()->format(TextFormat::GREEN . "---- " . TextFormat::RESET, TextFormat::GREEN . " ----" . TextFormat::RESET)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_chunks(TextFormat::RED . number_format($chunksCollected))->prefix(TextFormat::GOLD)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_entities(TextFormat::RED . number_format($entitiesCollected))->prefix(TextFormat::GOLD)); diff --git a/src/command/defaults/HelpCommand.php b/src/command/defaults/HelpCommand.php index 7fe12e039..4ed5d4275 100644 --- a/src/command/defaults/HelpCommand.php +++ b/src/command/defaults/HelpCommand.php @@ -95,7 +95,7 @@ class HelpCommand extends VanillaCommand{ foreach($commands[$pageNumber - 1] as $command){ $description = $command->getDescription(); $descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description; - $sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getName() . ": " . TextFormat::WHITE . $descriptionString); + $sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getName() . ": " . TextFormat::RESET . $descriptionString); } } @@ -107,18 +107,18 @@ class HelpCommand extends VanillaCommand{ $description = $cmd->getDescription(); $descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description; $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($cmd->getName()) - ->format(TextFormat::YELLOW . "--------- " . TextFormat::WHITE, TextFormat::YELLOW . " ---------")); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::WHITE . $descriptionString) + ->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------")); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString) ->prefix(TextFormat::GOLD)); $usage = $cmd->getUsage(); $usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage; - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::WHITE . implode("\n" . TextFormat::WHITE, explode("\n", $usageString))) + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString))) ->prefix(TextFormat::GOLD)); $aliases = $cmd->getAliases(); sort($aliases, SORT_NATURAL); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::WHITE . implode(", ", $aliases)) + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::RESET . implode(", ", $aliases)) ->prefix(TextFormat::GOLD)); return true; diff --git a/src/command/defaults/MeCommand.php b/src/command/defaults/MeCommand.php index 586d6cb39..82bec234f 100644 --- a/src/command/defaults/MeCommand.php +++ b/src/command/defaults/MeCommand.php @@ -52,7 +52,7 @@ class MeCommand extends VanillaCommand{ throw new InvalidCommandSyntaxException(); } - $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::WHITE . implode(" ", $args))); + $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args))); return true; } diff --git a/src/command/defaults/PluginsCommand.php b/src/command/defaults/PluginsCommand.php index e4c83354d..420b7708c 100644 --- a/src/command/defaults/PluginsCommand.php +++ b/src/command/defaults/PluginsCommand.php @@ -56,7 +56,7 @@ class PluginsCommand extends VanillaCommand{ }, $sender->getServer()->getPluginManager()->getPlugins()); sort($list, SORT_STRING); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_plugins_success((string) count($list), implode(TextFormat::WHITE . ", ", $list))); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_plugins_success((string) count($list), implode(TextFormat::RESET . ", ", $list))); return true; } } diff --git a/src/command/defaults/StatusCommand.php b/src/command/defaults/StatusCommand.php index 7e5f9b544..e97e55f95 100644 --- a/src/command/defaults/StatusCommand.php +++ b/src/command/defaults/StatusCommand.php @@ -52,7 +52,7 @@ class StatusCommand extends VanillaCommand{ $mUsage = Process::getAdvancedMemoryUsage(); $server = $sender->getServer(); - $sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----"); + $sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::RESET . "Server status" . TextFormat::GREEN . " ----"); $time = (int) (microtime(true) - $server->getStartTime()); diff --git a/src/command/defaults/VersionCommand.php b/src/command/defaults/VersionCommand.php index 135c8e780..487f84aff 100644 --- a/src/command/defaults/VersionCommand.php +++ b/src/command/defaults/VersionCommand.php @@ -57,17 +57,18 @@ class VersionCommand extends VanillaCommand{ if(count($args) === 0){ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareName( - VersionInfo::NAME + TextFormat::GREEN . VersionInfo::NAME . TextFormat::RESET )); + $versionColor = VersionInfo::IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : TextFormat::GREEN; $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareVersion( - VersionInfo::VERSION()->getFullVersion(), - VersionInfo::GIT_HASH() + $versionColor . VersionInfo::VERSION()->getFullVersion() . TextFormat::RESET, + TextFormat::GREEN . VersionInfo::GIT_HASH() . TextFormat::RESET )); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_minecraftVersion( - ProtocolInfo::MINECRAFT_VERSION_NETWORK, - (string) ProtocolInfo::CURRENT_PROTOCOL + TextFormat::GREEN . ProtocolInfo::MINECRAFT_VERSION_NETWORK . TextFormat::RESET, + TextFormat::GREEN . ProtocolInfo::CURRENT_PROTOCOL . TextFormat::RESET )); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpVersion(PHP_VERSION)); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpVersion(TextFormat::GREEN . PHP_VERSION . TextFormat::RESET)); $jitMode = Utils::getOpcacheJitMode(); if($jitMode !== null){ @@ -79,8 +80,8 @@ class VersionCommand extends VanillaCommand{ }else{ $jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitNotSupported(); } - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpJitStatus($jitStatus)); - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_operatingSystem(Utils::getOS())); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpJitStatus($jitStatus->format(TextFormat::GREEN, TextFormat::RESET))); + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_operatingSystem(TextFormat::GREEN . Utils::getOS() . TextFormat::RESET)); }else{ $pluginName = implode(" ", $args); $exactPlugin = $sender->getServer()->getPluginManager()->getPlugin($pluginName); @@ -110,7 +111,7 @@ class VersionCommand extends VanillaCommand{ private function describeToSender(Plugin $plugin, CommandSender $sender) : void{ $desc = $plugin->getDescription(); - $sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::WHITE . " version " . TextFormat::DARK_GREEN . $desc->getVersion()); + $sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::RESET . " version " . TextFormat::DARK_GREEN . $desc->getVersion()); if($desc->getDescription() !== ""){ $sender->sendMessage($desc->getDescription()); diff --git a/src/console/ConsoleCommandSender.php b/src/console/ConsoleCommandSender.php index 9b4f4191e..ef3683b0b 100644 --- a/src/console/ConsoleCommandSender.php +++ b/src/console/ConsoleCommandSender.php @@ -66,7 +66,7 @@ class ConsoleCommandSender implements CommandSender{ } foreach(explode("\n", trim($message)) as $line){ - Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::WHITE . $line); + Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line)); } } diff --git a/src/utils/MainLogger.php b/src/utils/MainLogger.php index 6cefce218..139388af7 100644 --- a/src/utils/MainLogger.php +++ b/src/utils/MainLogger.php @@ -191,7 +191,7 @@ class MainLogger extends \AttachableThreadedLogger implements \BufferedLogger{ $threadName = (new \ReflectionClass($thread))->getShortName() . " thread"; } - $message = sprintf($this->format, $time->format("H:i:s.v"), $color, $threadName, $prefix, TextFormat::clean($message, false)); + $message = sprintf($this->format, $time->format("H:i:s.v"), $color, $threadName, $prefix, TextFormat::addBase($color, TextFormat::clean($message, false))); if(!Terminal::isInit()){ Terminal::init($this->useFormattingCodes); //lazy-init colour codes because we don't know if they've been registered on this thread diff --git a/src/utils/TextFormat.php b/src/utils/TextFormat.php index d2250057f..dfd6a359a 100644 --- a/src/utils/TextFormat.php +++ b/src/utils/TextFormat.php @@ -158,6 +158,31 @@ abstract class TextFormat{ return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-gk-or])/u', TextFormat::ESCAPE . '$1', $string); } + /** + * Adds base formatting to the string. The given format codes will be inserted directly after any RESET (§r) codes. + * + * This is useful for log messages, where a RESET code should return to the log message's original colour (e.g. + * blue for NOTICE), rather than whatever the terminal's base text colour is (usually some off-white colour). + * + * Example behaviour: + * - Base format "§c" (red) + "Hello" (no format) = "§r§cHello" + * - Base format "§c" + "Hello §rWorld" = "§r§cHello §r§cWorld" + * + * Note: Adding base formatting to the output string a second time will result in a combination of formats from both + * calls. This is not by design, but simply a consequence of the way the function is implemented. + */ + public static function addBase(string $baseFormat, string $string) : string{ + $baseFormatParts = self::tokenize($baseFormat); + foreach($baseFormatParts as $part){ + if(!isset(self::FORMATS[$part]) && !isset(self::COLORS[$part])){ + throw new \InvalidArgumentException("Unexpected base format token \"$part\", expected only color and format tokens"); + } + } + $baseFormat = self::RESET . $baseFormat; + + return $baseFormat . str_replace(TextFormat::RESET, $baseFormat, $string); + } + /** * Returns an HTML-formatted string with colors/markup */ From 1c7b1e9e5d42bfd8d034ee225a91c173e6e14fe9 Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 <66992287+ShockedPlot7560@users.noreply.github.com> Date: Wed, 28 Sep 2022 17:38:24 +0200 Subject: [PATCH 22/53] Fix sugarcane behaviour on fertilizers (#4930) --- src/block/Sugarcane.php | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index fe1935c8b..80c758504 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -31,6 +31,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; +use pocketmine\world\Position; class Sugarcane extends Flowable{ public const MAX_AGE = 15; @@ -49,14 +50,23 @@ class Sugarcane extends Flowable{ return 0b1111; } - private function grow() : bool{ - $grew = false; + private function seekToBottom() : Position{ $world = $this->position->getWorld(); + $bottom = $this->position; + while(($next = $world->getBlock($bottom->down()))->isSameType($this)){ + $bottom = $next->position; + } + return $bottom; + } + + private function grow(Position $pos) : bool{ + $grew = false; + $world = $pos->getWorld(); for($y = 1; $y < 3; ++$y){ - if(!$world->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){ + if(!$world->isInWorld($pos->x, $pos->y + $y, $pos->z)){ break; } - $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); + $b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z); if($b->getId() === BlockLegacyIds::AIR){ $ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE()); $ev->call(); @@ -65,12 +75,12 @@ class Sugarcane extends Flowable{ } $world->setBlock($b->position, $ev->getNewState()); $grew = true; - }else{ + }elseif(!$b->isSameType($this)){ break; } } $this->age = 0; - $world->setBlock($this->position, $this); + $world->setBlock($pos, $this); return $grew; } @@ -87,7 +97,7 @@ class Sugarcane extends Flowable{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($item instanceof Fertilizer){ - if(!$this->getSide(Facing::DOWN)->isSameType($this) && $this->grow()){ + if($this->grow($this->seekToBottom())){ $item->pop(); } @@ -111,7 +121,7 @@ class Sugarcane extends Flowable{ public function onRandomTick() : void{ if(!$this->getSide(Facing::DOWN)->isSameType($this)){ if($this->age === self::MAX_AGE){ - $this->grow(); + $this->grow($this->position); }else{ ++$this->age; $this->position->getWorld()->setBlock($this->position, $this); From b21cd82e9456672a331f4187c2bcb66d3c6e6fa1 Mon Sep 17 00:00:00 2001 From: BrandPVP <114182697+BrandPVP@users.noreply.github.com> Date: Wed, 28 Sep 2022 19:27:33 +0300 Subject: [PATCH 23/53] Allow specifying a key for encrypted resource packs (#5297) --- resources/resource_packs.yml | 1 + .../handler/ResourcePacksPacketHandler.php | 14 ++++++++-- src/resourcepacks/ResourcePackManager.php | 26 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/resources/resource_packs.yml b/resources/resource_packs.yml index 39677852d..f236117d5 100644 --- a/resources/resource_packs.yml +++ b/resources/resource_packs.yml @@ -10,3 +10,4 @@ resource_stack: # - natural.zip # - vanilla.zip #If you want to force clients to use vanilla resources, you must place a vanilla resource pack in your resources folder and add it to the stack here. + #To specify a resource encryption key, put the key in the .key file alongside the resource pack. Example: vanilla.zip.key diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a7c603ceb..d1ba85724 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -65,9 +65,19 @@ class ResourcePacksPacketHandler extends PacketHandler{ ){} public function setUp() : void{ - $resourcePackEntries = array_map(static function(ResourcePack $pack) : ResourcePackInfoEntry{ + $resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{ //TODO: more stuff - return new ResourcePackInfoEntry($pack->getPackId(), $pack->getPackVersion(), $pack->getPackSize(), "", "", "", false); + $encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId()); + + return new ResourcePackInfoEntry( + $pack->getPackId(), + $pack->getPackVersion(), + $pack->getPackSize(), + $encryptionKey ?? "", + "", + $pack->getPackId(), + false + ); }, $this->resourcePackManager->getResourceStack()); //TODO: support forcing server packs $this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false)); diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 4aaa9afc7..f037f52ff 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace pocketmine\resourcepacks; +use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\utils\Config; use Webmozart\PathUtil\Path; use function array_keys; use function copy; use function count; use function file_exists; +use function file_get_contents; use function gettype; use function is_array; use function is_dir; @@ -49,6 +51,12 @@ class ResourcePackManager{ /** @var ResourcePack[] */ private array $uuidList = []; + /** + * @var string[] + * @phpstan-var array + */ + private array $encryptionKeys = []; + /** * @param string $path Path to resource-packs directory. */ @@ -106,6 +114,17 @@ class ResourcePackManager{ if($newPack instanceof ResourcePack){ $this->resourcePacks[] = $newPack; $this->uuidList[strtolower($newPack->getPackId())] = $newPack; + + $keyPath = Path::join($this->path, $pack . ".key"); + if(file_exists($keyPath)){ + try{ + $this->encryptionKeys[strtolower($newPack->getPackId())] = ErrorToExceptionHandler::trapAndRemoveFalse( + fn() => file_get_contents($keyPath) + ); + }catch(\ErrorException $e){ + throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $e); + } + } }else{ throw new ResourcePackException("Format not recognized"); } @@ -153,4 +172,11 @@ class ResourcePackManager{ public function getPackIdList() : array{ return array_keys($this->uuidList); } + + /** + * Returns the key with which the pack was encrypted, or null if the pack has no key. + */ + public function getPackEncryptionKey(string $id) : ?string{ + return $this->encryptionKeys[strtolower($id)] ?? null; + } } From bda0ca23b497bcfae33de6adc0ef177db10dd7ce Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 Sep 2022 17:34:51 +0100 Subject: [PATCH 24/53] Living: deprecated hasLineOfSight() --- src/entity/Living.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/entity/Living.php b/src/entity/Living.php index 58b7c1791..890b6e740 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -294,6 +294,10 @@ abstract class Living extends Entity{ return $nbt; } + /** + * @deprecated This function always returns true, no matter whether the target is in the line of sight or not. + * @see VoxelRayTrace::inDirection() for a more generalized method of ray-tracing to a target. + */ public function hasLineOfSight(Entity $entity) : bool{ //TODO: head height return true; From d6bbf8217dfb47054e32acf689702a70c7d508d8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 Sep 2022 20:57:17 +0100 Subject: [PATCH 25/53] ResourcePackManager: avoid repeated operation --- src/resourcepacks/ResourcePackManager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index f037f52ff..e6b37543a 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -113,12 +113,13 @@ class ResourcePackManager{ if($newPack instanceof ResourcePack){ $this->resourcePacks[] = $newPack; - $this->uuidList[strtolower($newPack->getPackId())] = $newPack; + $index = strtolower($newPack->getPackId()); + $this->uuidList[$index] = $newPack; $keyPath = Path::join($this->path, $pack . ".key"); if(file_exists($keyPath)){ try{ - $this->encryptionKeys[strtolower($newPack->getPackId())] = ErrorToExceptionHandler::trapAndRemoveFalse( + $this->encryptionKeys[$index] = ErrorToExceptionHandler::trapAndRemoveFalse( fn() => file_get_contents($keyPath) ); }catch(\ErrorException $e){ From 0c7f8470b945db3219428c807f186eec9f1aa503 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 Sep 2022 21:30:06 +0100 Subject: [PATCH 26/53] Avoid repeated strtolower usages in a couple of places --- src/command/SimpleCommandMap.php | 5 +++-- src/item/LegacyStringToItemParser.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php index 5c33bf25a..07afecf6d 100644 --- a/src/command/SimpleCommandMap.php +++ b/src/command/SimpleCommandMap.php @@ -272,10 +272,11 @@ class SimpleCommandMap implements CommandMap{ } //These registered commands have absolute priority + $lowerAlias = strtolower($alias); if(count($targets) > 0){ - $this->knownCommands[strtolower($alias)] = new FormattedCommandAlias(strtolower($alias), $targets); + $this->knownCommands[$lowerAlias] = new FormattedCommandAlias($lowerAlias, $targets); }else{ - unset($this->knownCommands[strtolower($alias)]); + unset($this->knownCommands[$lowerAlias]); } } diff --git a/src/item/LegacyStringToItemParser.php b/src/item/LegacyStringToItemParser.php index e05f4d031..3d69dd7ac 100644 --- a/src/item/LegacyStringToItemParser.php +++ b/src/item/LegacyStringToItemParser.php @@ -108,8 +108,9 @@ final class LegacyStringToItemParser{ throw new LegacyStringToItemParserException("Unable to parse \"" . $b[1] . "\" from \"" . $input . "\" as a valid meta value"); } - if(isset($this->map[strtolower($b[0])])){ - $item = $this->itemFactory->get($this->map[strtolower($b[0])], $meta); + $id = strtolower($b[0]); + if(isset($this->map[$id])){ + $item = $this->itemFactory->get($this->map[$id], $meta); }else{ throw new LegacyStringToItemParserException("Unable to resolve \"" . $input . "\" to a valid item"); } From 41970feb573bb27a0372b9b13451ac6e97ad479b Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 29 Sep 2022 22:30:12 +0100 Subject: [PATCH 27/53] Entity: Fire EntitySpawnEvent/ItemSpawnEvent on the first entity tick, instead of in the constructor (#5314) This allows plugins to modify the entity via setters in EntitySpawnEvent without their changes getting overwritten by setter calls directly after the 'new YourEntity' statement. As well as benefiting plugins, this also clears a path for a BC-breaking change in PM5 (to have the programmer use addEntity() to spawn entities, instead of the constructor doing it, which will improve on a number of data handling aspects). fixes #4973 This targets next-minor because it has some side effects on plugins that depended on the old behaviour, such as VanillaHopper, so it's not suitable for a patch release. --- src/entity/Entity.php | 14 ++++++++++++-- src/entity/object/ItemEntity.php | 5 ++++- src/player/Player.php | 4 ++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 54873f9f1..3e34d5c26 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -249,10 +249,8 @@ abstract class Entity{ $this->getWorld()->addEntity($this); $this->lastUpdate = $this->server->getTick(); - (new EntitySpawnEvent($this))->call(); $this->scheduleUpdate(); - } abstract protected function getInitialSizeInfo() : EntitySizeInfo; @@ -937,6 +935,14 @@ abstract class Entity{ return (new Vector2(-cos(deg2rad($this->location->yaw) - M_PI_2), -sin(deg2rad($this->location->yaw) - M_PI_2)))->normalize(); } + /** + * Called from onUpdate() on the first tick of a new entity. This is called before any movement processing or + * main ticking logic. Use this to fire any events related to spawning the entity. + */ + protected function onFirstUpdate(int $currentTick) : void{ + (new EntitySpawnEvent($this))->call(); + } + public function onUpdate(int $currentTick) : bool{ if($this->closed){ return false; @@ -953,6 +959,10 @@ abstract class Entity{ $this->lastUpdate = $currentTick; + if($this->justCreated){ + $this->onFirstUpdate($currentTick); + } + if(!$this->isAlive()){ if($this->onDeathUpdate($tickDiff)){ $this->flagForDespawn(); diff --git a/src/entity/object/ItemEntity.php b/src/entity/object/ItemEntity.php index 5c39f3bec..4fe844f7e 100644 --- a/src/entity/object/ItemEntity.php +++ b/src/entity/object/ItemEntity.php @@ -92,8 +92,11 @@ class ItemEntity extends Entity{ $this->pickupDelay = $nbt->getShort("PickupDelay", $this->pickupDelay); $this->owner = $nbt->getString("Owner", $this->owner); $this->thrower = $nbt->getString("Thrower", $this->thrower); + } - (new ItemSpawnEvent($this))->call(); + protected function onFirstUpdate(int $currentTick) : void{ + (new ItemSpawnEvent($this))->call(); //this must be called before EntitySpawnEvent, to maintain backwards compatibility + parent::onFirstUpdate($currentTick); } protected function entityBaseTick(int $tickDiff = 1) : bool{ diff --git a/src/player/Player.php b/src/player/Player.php index 340452b31..157038beb 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1343,6 +1343,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->lastUpdate = $currentTick; + if($this->justCreated){ + $this->onFirstUpdate($currentTick); + } + if(!$this->isAlive() && $this->spawned){ $this->onDeathUpdate($tickDiff); return true; From 3feaa18f6c10c3a99c0deca75f57ec2d74b92ab4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 30 Sep 2022 15:12:37 +0100 Subject: [PATCH 28/53] DelegateInventory: use WeakReference and __destruct to clean up inventory listener this is more sane, since it allows the delegate to be reused without unexpected behaviour. --- src/inventory/DelegateInventory.php | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/inventory/DelegateInventory.php b/src/inventory/DelegateInventory.php index db65b7a16..391b9599c 100644 --- a/src/inventory/DelegateInventory.php +++ b/src/inventory/DelegateInventory.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\item\Item; -use pocketmine\player\Player; -use function count; /** * An inventory which is backed by another inventory, and acts as a proxy to that inventory. @@ -37,16 +35,25 @@ class DelegateInventory extends BaseInventory{ private Inventory $backingInventory ){ parent::__construct(); + $weakThis = \WeakReference::create($this); $this->backingInventory->getListeners()->add($this->inventoryListener = new CallbackInventoryListener( - function(Inventory $unused, int $slot, Item $oldItem) : void{ - $this->onSlotChange($slot, $oldItem); + static function(Inventory $unused, int $slot, Item $oldItem) use ($weakThis) : void{ + if(($strongThis = $weakThis->get()) !== null){ + $strongThis->onSlotChange($slot, $oldItem); + } }, - function(Inventory $unused, array $oldContents) : void{ - $this->onContentChange($oldContents); + static function(Inventory $unused, array $oldContents) use ($weakThis) : void{ + if(($strongThis = $weakThis->get()) !== null){ + $strongThis->onContentChange($oldContents); + } } )); } + public function __destruct(){ + $this->backingInventory->getListeners()->remove($this->inventoryListener); + } + public function getSize() : int{ return $this->backingInventory->getSize(); } @@ -66,12 +73,4 @@ class DelegateInventory extends BaseInventory{ protected function internalSetContents(array $items) : void{ $this->backingInventory->setContents($items); } - - public function onClose(Player $who) : void{ - parent::onClose($who); - if(count($this->getViewers()) === 0 && count($this->getListeners()->toArray()) === 1){ - $this->backingInventory->getListeners()->remove($this->inventoryListener); - $this->inventoryListener = CallbackInventoryListener::onAnyChange(static function() : void{}); //break cyclic reference - } - } } From 2cc8a56e6851c01d35fec495c0e94d625fc6c406 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Oct 2022 20:02:44 +0100 Subject: [PATCH 29/53] Server: fixed borked serverCommand timings --- src/Server.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Server.php b/src/Server.php index cccbdf60b..2c3f5b85d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1850,10 +1850,12 @@ class Server{ $this->getMemoryManager()->check(); if($this->console !== null){ + Timings::$serverCommand->startTiming(); while(($line = $this->console->readLine()) !== null){ $this->consoleSender ??= new ConsoleCommandSender($this, $this->language); $this->dispatchCommand($this->consoleSender, $line); } + Timings::$serverCommand->stopTiming(); } Timings::$serverTick->stopTiming(); From b3bda788d966603db658950ea442d997403c2720 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Oct 2022 21:45:09 +0100 Subject: [PATCH 30/53] Server: Deprecated getPlayerByPrefix() this is only used for commands anyway, but we can't get rid of it yet. --- src/Server.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Server.php b/src/Server.php index 2c3f5b85d..34de10b9e 100644 --- a/src/Server.php +++ b/src/Server.php @@ -609,6 +609,10 @@ class Server{ } /** + * @deprecated This method's results are unpredictable. The string "Steve" will return the player named "SteveJobs", + * until another player named "Steve" joins the server, at which point it will return that player instead. Use + * {@link Server::getPlayersByPrefix()} instead. + * * Returns an online player whose name begins with or equals the given string (case insensitive). * The closest match will be returned, or null if there are no online matches. * From 7bcc663b609623f6016cc7f6a544358cd3753f71 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Oct 2022 21:51:29 +0100 Subject: [PATCH 31/53] Migrate core code to using symfony/filesystem webmozart/path-util is retained for plugin compatibility, but is dropped in 5.0 --- build/generate-known-translation-apis.php | 2 +- composer.json | 1 + composer.lock | 314 +++++++++++++++++- src/MemoryManager.php | 2 +- src/PocketMine.php | 2 +- src/Server.php | 2 +- src/command/defaults/DumpMemoryCommand.php | 2 +- src/command/defaults/TimingsCommand.php | 2 +- .../ConsoleReaderChildProcessDaemon.php | 2 +- src/crash/CrashDump.php | 2 +- .../bedrock/LegacyBiomeIdToStringIdMap.php | 2 +- .../bedrock/LegacyBlockIdToStringIdMap.php | 2 +- .../bedrock/LegacyEntityIdToStringIdMap.php | 2 +- .../bedrock/LegacyItemIdToStringIdMap.php | 2 +- src/inventory/CreativeInventory.php | 2 +- src/item/LegacyStringToItemParser.php | 2 +- src/lang/Language.php | 2 +- src/network/mcpe/cache/StaticPacketCache.php | 2 +- .../mcpe/convert/GlobalItemTypeDictionary.php | 2 +- src/network/mcpe/convert/ItemTranslator.php | 2 +- .../mcpe/convert/RuntimeBlockMapping.php | 2 +- src/plugin/PluginBase.php | 2 +- src/plugin/PluginManager.php | 2 +- src/resourcepacks/ResourcePackManager.php | 2 +- src/scheduler/DumpWorkerMemoryTask.php | 2 +- src/utils/Config.php | 2 +- src/utils/Filesystem.php | 2 +- src/wizard/SetupWizard.php | 2 +- src/world/WorldManager.php | 2 +- src/world/format/io/FormatConverter.php | 2 +- src/world/format/io/data/BedrockWorldData.php | 2 +- src/world/format/io/data/JavaWorldData.php | 2 +- src/world/format/io/leveldb/LevelDB.php | 2 +- .../format/io/region/RegionWorldProvider.php | 2 +- .../io/region/WritableRegionWorldProvider.php | 2 +- .../format/io/region/RegionLoaderTest.php | 2 +- tools/generate-permission-doc.php | 2 +- tools/simulate-chunk-selector.php | 2 +- 38 files changed, 350 insertions(+), 37 deletions(-) diff --git a/build/generate-known-translation-apis.php b/build/generate-known-translation-apis.php index e799735c0..af617c523 100644 --- a/build/generate-known-translation-apis.php +++ b/build/generate-known-translation-apis.php @@ -25,7 +25,7 @@ namespace pocketmine\build\generate_known_translation_apis; use pocketmine\lang\Translatable; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_map; use function count; use function dirname; diff --git a/composer.json b/composer.json index 7b2a8614f..5ad75593d 100644 --- a/composer.json +++ b/composer.json @@ -50,6 +50,7 @@ "pocketmine/raklib-ipc": "^0.1.0", "pocketmine/snooze": "^0.3.0", "ramsey/uuid": "^4.1", + "symfony/filesystem": "^5.4", "webmozart/path-util": "^2.3" }, "require-dev": { diff --git a/composer.lock b/composer.lock index d557c21db..daa8ca40c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8153377a26bfd4a342a9b7101580f1ea", + "content-hash": "2c6e95f77e3aa27ede46bd06716e178b", "packages": [ { "name": "adhocore/json-comment", @@ -1022,6 +1022,318 @@ ], "time": "2022-09-16T03:22:46+00:00" }, + { + "name": "symfony/filesystem", + "version": "v5.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T19:53:16+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, { "name": "symfony/polyfill-php81", "version": "v1.26.0", diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 49a728c89..cf7bc745d 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -30,7 +30,7 @@ use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\timings\Timings; use pocketmine\utils\Process; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function arsort; use function count; use function fclose; diff --git a/src/PocketMine.php b/src/PocketMine.php index b2e9a57a7..faccb27e1 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -34,7 +34,7 @@ namespace pocketmine { use pocketmine\utils\Timezone; use pocketmine\utils\Utils; use pocketmine\wizard\SetupWizard; - use Webmozart\PathUtil\Path; + use Symfony\Component\Filesystem\Path; use function defined; use function extension_loaded; use function function_exists; diff --git a/src/Server.php b/src/Server.php index 34de10b9e..0a2f64c88 100644 --- a/src/Server.php +++ b/src/Server.php @@ -115,7 +115,7 @@ use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; use pocketmine\world\WorldManager; use Ramsey\Uuid\UuidInterface; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_sum; use function base64_encode; use function cli_set_process_title; diff --git a/src/command/defaults/DumpMemoryCommand.php b/src/command/defaults/DumpMemoryCommand.php index 83f177e01..2e425a740 100644 --- a/src/command/defaults/DumpMemoryCommand.php +++ b/src/command/defaults/DumpMemoryCommand.php @@ -25,7 +25,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\permission\DefaultPermissionNames; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function date; class DumpMemoryCommand extends VanillaCommand{ diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php index 14fae62f5..ecfa7b281 100644 --- a/src/command/defaults/TimingsCommand.php +++ b/src/command/defaults/TimingsCommand.php @@ -35,7 +35,7 @@ use pocketmine\timings\TimingsHandler; use pocketmine\utils\InternetException; use pocketmine\utils\InternetRequestResult; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function count; use function fclose; use function file_exists; diff --git a/src/console/ConsoleReaderChildProcessDaemon.php b/src/console/ConsoleReaderChildProcessDaemon.php index a0c689d37..138559f06 100644 --- a/src/console/ConsoleReaderChildProcessDaemon.php +++ b/src/console/ConsoleReaderChildProcessDaemon.php @@ -25,7 +25,7 @@ namespace pocketmine\console; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function base64_encode; use function fgets; use function fopen; diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index e724ea217..e743a6704 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -33,7 +33,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use pocketmine\VersionInfo; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function base64_encode; use function error_get_last; use function file; diff --git a/src/data/bedrock/LegacyBiomeIdToStringIdMap.php b/src/data/bedrock/LegacyBiomeIdToStringIdMap.php index 974792eba..eba0034e8 100644 --- a/src/data/bedrock/LegacyBiomeIdToStringIdMap.php +++ b/src/data/bedrock/LegacyBiomeIdToStringIdMap.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; final class LegacyBiomeIdToStringIdMap extends LegacyToStringBidirectionalIdMap{ use SingletonTrait; diff --git a/src/data/bedrock/LegacyBlockIdToStringIdMap.php b/src/data/bedrock/LegacyBlockIdToStringIdMap.php index 616c56bcf..35c24caf9 100644 --- a/src/data/bedrock/LegacyBlockIdToStringIdMap.php +++ b/src/data/bedrock/LegacyBlockIdToStringIdMap.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; final class LegacyBlockIdToStringIdMap extends LegacyToStringBidirectionalIdMap{ use SingletonTrait; diff --git a/src/data/bedrock/LegacyEntityIdToStringIdMap.php b/src/data/bedrock/LegacyEntityIdToStringIdMap.php index 2e3e4aecc..8427e4594 100644 --- a/src/data/bedrock/LegacyEntityIdToStringIdMap.php +++ b/src/data/bedrock/LegacyEntityIdToStringIdMap.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; final class LegacyEntityIdToStringIdMap extends LegacyToStringBidirectionalIdMap{ use SingletonTrait; diff --git a/src/data/bedrock/LegacyItemIdToStringIdMap.php b/src/data/bedrock/LegacyItemIdToStringIdMap.php index 85b6ff1bf..254ad96bb 100644 --- a/src/data/bedrock/LegacyItemIdToStringIdMap.php +++ b/src/data/bedrock/LegacyItemIdToStringIdMap.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; final class LegacyItemIdToStringIdMap extends LegacyToStringBidirectionalIdMap{ use SingletonTrait; diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 40b3075d2..980c2e35f 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -26,7 +26,7 @@ namespace pocketmine\inventory; use pocketmine\item\Durable; use pocketmine\item\Item; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_get_contents; use function json_decode; diff --git a/src/item/LegacyStringToItemParser.php b/src/item/LegacyStringToItemParser.php index 3d69dd7ac..9ec97ede8 100644 --- a/src/item/LegacyStringToItemParser.php +++ b/src/item/LegacyStringToItemParser.php @@ -26,7 +26,7 @@ namespace pocketmine\item; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function explode; use function file_get_contents; use function is_array; diff --git a/src/lang/Language.php b/src/lang/Language.php index 7e6f6cfcc..3c61a0cdd 100644 --- a/src/lang/Language.php +++ b/src/lang/Language.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\lang; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_filter; use function array_map; use function count; diff --git a/src/network/mcpe/cache/StaticPacketCache.php b/src/network/mcpe/cache/StaticPacketCache.php index b4e1a7150..c3eca501d 100644 --- a/src/network/mcpe/cache/StaticPacketCache.php +++ b/src/network/mcpe/cache/StaticPacketCache.php @@ -28,7 +28,7 @@ use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\utils\SingletonTrait; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_get_contents; class StaticPacketCache{ diff --git a/src/network/mcpe/convert/GlobalItemTypeDictionary.php b/src/network/mcpe/convert/GlobalItemTypeDictionary.php index 6940a91d7..f33683346 100644 --- a/src/network/mcpe/convert/GlobalItemTypeDictionary.php +++ b/src/network/mcpe/convert/GlobalItemTypeDictionary.php @@ -28,7 +28,7 @@ use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_get_contents; use function is_array; use function is_bool; diff --git a/src/network/mcpe/convert/ItemTranslator.php b/src/network/mcpe/convert/ItemTranslator.php index 4a2036e0b..30e3d6b92 100644 --- a/src/network/mcpe/convert/ItemTranslator.php +++ b/src/network/mcpe/convert/ItemTranslator.php @@ -28,7 +28,7 @@ use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_key_exists; use function file_get_contents; use function is_array; diff --git a/src/network/mcpe/convert/RuntimeBlockMapping.php b/src/network/mcpe/convert/RuntimeBlockMapping.php index 8a47de971..547bd0d81 100644 --- a/src/network/mcpe/convert/RuntimeBlockMapping.php +++ b/src/network/mcpe/convert/RuntimeBlockMapping.php @@ -32,7 +32,7 @@ use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_get_contents; /** diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index e9bdc2f20..9b0ac2399 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -33,7 +33,7 @@ use pocketmine\Server; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Config; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function count; use function dirname; use function fclose; diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 4fdd7ee76..fc16fa7ea 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -40,7 +40,7 @@ use pocketmine\Server; use pocketmine\timings\TimingsHandler; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_diff_key; use function array_key_exists; use function array_keys; diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index e6b37543a..d1482d8b6 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -25,7 +25,7 @@ namespace pocketmine\resourcepacks; use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\utils\Config; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_keys; use function copy; use function count; diff --git a/src/scheduler/DumpWorkerMemoryTask.php b/src/scheduler/DumpWorkerMemoryTask.php index 98b5e8909..b1cf3840c 100644 --- a/src/scheduler/DumpWorkerMemoryTask.php +++ b/src/scheduler/DumpWorkerMemoryTask.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\scheduler; use pocketmine\MemoryManager; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; /** * Task used to dump memory from AsyncWorkers diff --git a/src/utils/Config.php b/src/utils/Config.php index ffc2db69a..306f9eb80 100644 --- a/src/utils/Config.php +++ b/src/utils/Config.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\utils; use pocketmine\errorhandler\ErrorToExceptionHandler; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_change_key_case; use function array_fill_keys; use function array_keys; diff --git a/src/utils/Filesystem.php b/src/utils/Filesystem.php index 20ec8b312..a4c1ff236 100644 --- a/src/utils/Filesystem.php +++ b/src/utils/Filesystem.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\utils; use pocketmine\errorhandler\ErrorToExceptionHandler; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function copy; use function dirname; use function fclose; diff --git a/src/wizard/SetupWizard.php b/src/wizard/SetupWizard.php index 8373b0e8b..c9170bd8d 100644 --- a/src/wizard/SetupWizard.php +++ b/src/wizard/SetupWizard.php @@ -39,7 +39,7 @@ use pocketmine\utils\Internet; use pocketmine\utils\InternetException; use pocketmine\utils\Utils; use pocketmine\VersionInfo; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function fgets; use function sleep; use function strtolower; diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php index 83f7aac98..1c26d494b 100644 --- a/src/world/WorldManager.php +++ b/src/world/WorldManager.php @@ -39,7 +39,7 @@ use pocketmine\world\format\io\WorldProviderManager; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\generator\InvalidGeneratorOptionsException; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_keys; use function array_shift; use function assert; diff --git a/src/world/format/io/FormatConverter.php b/src/world/format/io/FormatConverter.php index 48f4d67b9..5f93f3a7b 100644 --- a/src/world/format/io/FormatConverter.php +++ b/src/world/format/io/FormatConverter.php @@ -27,7 +27,7 @@ use pocketmine\utils\Filesystem; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\generator\normal\Normal; use pocketmine\world\WorldCreationOptions; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function basename; use function crc32; use function file_exists; diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index b93acac68..995f64898 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -38,7 +38,7 @@ use pocketmine\world\generator\Flat; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_get_contents; use function file_put_contents; use function strlen; diff --git a/src/world/format/io/data/JavaWorldData.php b/src/world/format/io/data/JavaWorldData.php index e57bbe941..e53d857ad 100644 --- a/src/world/format/io/data/JavaWorldData.php +++ b/src/world/format/io/data/JavaWorldData.php @@ -35,7 +35,7 @@ use pocketmine\world\format\io\exception\CorruptedWorldException; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function ceil; use function file_get_contents; use function file_put_contents; diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 5adcd0103..c2204582a 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -50,7 +50,7 @@ use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\SubChunk; use pocketmine\world\WorldCreationOptions; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function array_map; use function array_values; use function chr; diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index 91a7937a6..f02b1d790 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -32,7 +32,7 @@ use pocketmine\world\format\io\ChunkData; use pocketmine\world\format\io\data\JavaWorldData; use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\WorldData; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function assert; use function file_exists; use function is_dir; diff --git a/src/world/format/io/region/WritableRegionWorldProvider.php b/src/world/format/io/region/WritableRegionWorldProvider.php index fd4399474..56cc2ff71 100644 --- a/src/world/format/io/region/WritableRegionWorldProvider.php +++ b/src/world/format/io/region/WritableRegionWorldProvider.php @@ -27,7 +27,7 @@ use pocketmine\world\format\io\ChunkData; use pocketmine\world\format\io\data\JavaWorldData; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\WorldCreationOptions; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function file_exists; use function mkdir; diff --git a/tests/phpunit/world/format/io/region/RegionLoaderTest.php b/tests/phpunit/world/format/io/region/RegionLoaderTest.php index abf73a0f5..e4db90072 100644 --- a/tests/phpunit/world/format/io/region/RegionLoaderTest.php +++ b/tests/phpunit/world/format/io/region/RegionLoaderTest.php @@ -25,7 +25,7 @@ namespace pocketmine\world\format\io\region; use PHPUnit\Framework\TestCase; use pocketmine\world\format\ChunkException; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function bin2hex; use function clearstatcache; use function file_exists; diff --git a/tools/generate-permission-doc.php b/tools/generate-permission-doc.php index 7d66740aa..fd04f1a29 100644 --- a/tools/generate-permission-doc.php +++ b/tools/generate-permission-doc.php @@ -27,7 +27,7 @@ use pocketmine\permission\DefaultPermissions; use pocketmine\permission\PermissionManager; use pocketmine\utils\Utils; use pocketmine\VersionInfo; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function count; use function dirname; use function fclose; diff --git a/tools/simulate-chunk-selector.php b/tools/simulate-chunk-selector.php index e360a96e9..81beb6bb3 100644 --- a/tools/simulate-chunk-selector.php +++ b/tools/simulate-chunk-selector.php @@ -28,7 +28,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use pocketmine\world\format\Chunk; use pocketmine\world\World; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; use function assert; use function count; use function dirname; From b74c092d9b939671ba669ff59444e99b4af57fdd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Oct 2022 15:06:43 +0100 Subject: [PATCH 32/53] Server: removed reference to nonexistent API method --- src/Server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Server.php b/src/Server.php index 0a2f64c88..147e5a00b 100644 --- a/src/Server.php +++ b/src/Server.php @@ -610,8 +610,8 @@ class Server{ /** * @deprecated This method's results are unpredictable. The string "Steve" will return the player named "SteveJobs", - * until another player named "Steve" joins the server, at which point it will return that player instead. Use - * {@link Server::getPlayersByPrefix()} instead. + * until another player named "SteveJ" joins the server, at which point it will return that player instead. Prefer + * filtering the results of {@link Server::getOnlinePlayers()} yourself. * * Returns an online player whose name begins with or equals the given string (case insensitive). * The closest match will be returned, or null if there are no online matches. From 9a0ead6debba8501edcf9af0b556a2e704ce4dce Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 21 Oct 2022 14:28:22 +0100 Subject: [PATCH 33/53] Validate paths in --data and --plugins closes #2861 --- src/PocketMine.php | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/PocketMine.php b/src/PocketMine.php index faccb27e1..d3493f9f1 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -201,6 +201,24 @@ JIT_WARNING ini_set('assert.exception', '1'); } + function getopt_string(string $opt) : ?string{ + $opts = getopt("", ["$opt:"]); + if(isset($opts[$opt])){ + if(is_string($opts[$opt])){ + return $opts[$opt]; + } + if(is_array($opts[$opt])){ + critical_error("Cannot specify --$opt multiple times"); + exit(1); + } + if($opts[$opt] === false){ + critical_error("Missing value for --$opt"); + exit(1); + } + } + return null; + } + /** * @return void */ @@ -252,16 +270,22 @@ JIT_WARNING ErrorToExceptionHandler::set(); - $opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]); - $cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd()))); - $dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR; - $pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR; + $dataPath = getopt_string("data") ?? $cwd; + $pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins"; Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX); - if(!file_exists($dataPath)){ - mkdir($dataPath, 0777, true); + if(!@mkdir($dataPath, 0777, true) && (!is_dir($dataPath) || !is_writable($dataPath))){ + critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user."); + exit(1); } + //this has to be done after we're sure the data path exists + $dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR; + if(!@mkdir($pluginPath, 0777, true) && (!is_dir($pluginPath) || !is_writable($pluginPath))){ + critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user."); + exit(1); + } + $pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR; $lockFilePath = Path::join($dataPath, 'server.lock'); if(($pid = Filesystem::createLockFile($lockFilePath)) !== null){ @@ -273,6 +297,7 @@ JIT_WARNING //Logger has a dependency on timezone Timezone::init(); + $opts = getopt("", ["no-wizard", "enable-ansi", "disable-ansi"]); if(isset($opts["enable-ansi"])){ Terminal::init(true); }elseif(isset($opts["disable-ansi"])){ From c19880e045e3d76baa4e3398ea279d319719998e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 4 Nov 2022 20:50:27 +0000 Subject: [PATCH 34/53] bootstrap: fix PHPStan error --- src/PocketMine.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/PocketMine.php b/src/PocketMine.php index d3493f9f1..d84cf50a2 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -209,12 +209,10 @@ JIT_WARNING } if(is_array($opts[$opt])){ critical_error("Cannot specify --$opt multiple times"); - exit(1); - } - if($opts[$opt] === false){ + }else{ critical_error("Missing value for --$opt"); - exit(1); } + exit(1); } return null; } From ed452b9ccdd11f5168d8df7b63d43a11193cc851 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 4 Nov 2022 20:51:22 +0000 Subject: [PATCH 35/53] Scrub PHPStan baselines --- tests/phpstan/configs/actual-problems.neon | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 1121cd134..0afae17b6 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -15,21 +15,11 @@ parameters: count: 1 path: ../../../build/server-phar.php - - - message: "#^Binary operation \"\\.\" between array\\\\|string\\|false and '/'\\|'\\\\\\\\' results in an error\\.$#" - count: 2 - path: ../../../src/PocketMine.php - - message: "#^Do\\-while loop condition is always false\\.$#" count: 1 path: ../../../src/PocketMine.php - - - message: "#^Cannot cast mixed to string\\.$#" - count: 1 - path: ../../../src/Server.php - - message: "#^Parameter \\#1 \\$array of static method pocketmine\\\\plugin\\\\PluginGraylist\\:\\:fromArray\\(\\) expects array, mixed given\\.$#" count: 1 From bf7014e0eca06bc5d00205967ef3d16792af4036 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 7 Nov 2022 15:18:05 +0000 Subject: [PATCH 36/53] Release 4.11.0-BETA1 --- changelogs/4.11-beta.md | 67 +++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +-- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 changelogs/4.11-beta.md diff --git a/changelogs/4.11-beta.md b/changelogs/4.11-beta.md new file mode 100644 index 000000000..9da45f30c --- /dev/null +++ b/changelogs/4.11-beta.md @@ -0,0 +1,67 @@ +**For Minecraft: Bedrock Edition 1.19.40** + +This is a minor feature release for PocketMine-MP, introducing some new features and improvements. + +### Note about API versions +Plugins which don't touch the protocol and 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 protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 4.11.0-BETA1 +Released 7th November 2022. + +## General +- Packet receive timings have now been split into two subcategories - Decode and Handle. +- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`. + - Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway. + - Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess. +- Console command output now appears on the terminal only, and is not written to the log file. +- The output from console commands now appears with a `Command output |` prefix, instead of as a log message. +- Introduced validation for the `--data` and `--plugins` command line options. +- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder. + - e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key` + +## Gameplay +- Fixed supporting blocks of dead bush to be in line with vanilla. +- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block. +- Fixed modifier values for Instant Damage and Regeneration effects. + +## API +### General +- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`). +- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5. + - To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release. + +### `pocketmine` +- The following API methods are now deprecated: + - `Server->getPlayerByPrefix()` + +### `pocketmine\entity` +- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created. +- The following API methods are now deprecated: + - `Living->hasLineOfSight()` + +### `pocketmine\item` +- The following new API methods have been added: + - `public Armor->clearCustomColor() : $this` + +### `pocketmine\inventory\transaction` +- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s. + +### `pocketmine\player` +- The following new API methods have been added: + - `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message + +### `pocketmine\utils` +- The following new API methods have been added: + - `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code + +## Internals +- Improved performance of `ContainerTrait` dropping items on block destroy. (24e72ec109c1442b09558df89b6833cf2f2e0ec7) +- Avoid repeated calls to `Position->getWorld()` (use local variables). (2940547026db40ce76deb46e992870de3ead79ad) +- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. (e90abecf38d9c57635fa0497514bba7e546a2469) +- Console polling is now done on the main thread (no longer a performance concern). +- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. +- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. +- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index fc590de79..4f8e3cb16 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.10.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "4.11.0-BETA1"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "beta"; private function __construct(){ From 069062f122f5aa1d1a97aaebd8ea896c0ceca490 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 7 Nov 2022 15:18:09 +0000 Subject: [PATCH 37/53] 4.11.0-BETA2 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 4f8e3cb16..cfc5ddb12 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.11.0-BETA1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "4.11.0-BETA2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "beta"; private function __construct(){ From d03bbb0426e6c531712a451928b5dd5cec43b8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Le=C3=B3n?= <58715544+JavierLeon9966@users.noreply.github.com> Date: Thu, 10 Nov 2022 09:57:14 -0300 Subject: [PATCH 38/53] Implemented darkness effect (#5402) --- src/data/bedrock/EffectIdMap.php | 1 + src/data/bedrock/EffectIds.php | 1 + src/entity/effect/StringToEffectParser.php | 1 + src/entity/effect/VanillaEffects.php | 2 ++ 4 files changed, 5 insertions(+) diff --git a/src/data/bedrock/EffectIdMap.php b/src/data/bedrock/EffectIdMap.php index 23985fa12..6dce86d9b 100644 --- a/src/data/bedrock/EffectIdMap.php +++ b/src/data/bedrock/EffectIdMap.php @@ -74,6 +74,7 @@ final class EffectIdMap{ //TODO: SLOW_FALLING //TODO: BAD_OMEN //TODO: VILLAGE_HERO + $this->register(EffectIds::DARKNESS, VanillaEffects::DARKNESS()); } //TODO: not a big fan of the code duplication here :( diff --git a/src/data/bedrock/EffectIds.php b/src/data/bedrock/EffectIds.php index 3acf56569..a2ada01d9 100644 --- a/src/data/bedrock/EffectIds.php +++ b/src/data/bedrock/EffectIds.php @@ -58,4 +58,5 @@ final class EffectIds{ public const SLOW_FALLING = 27; public const BAD_OMEN = 28; public const VILLAGE_HERO = 29; + public const DARKNESS = 30; } diff --git a/src/entity/effect/StringToEffectParser.php b/src/entity/effect/StringToEffectParser.php index d336af33b..23bd29bd0 100644 --- a/src/entity/effect/StringToEffectParser.php +++ b/src/entity/effect/StringToEffectParser.php @@ -40,6 +40,7 @@ final class StringToEffectParser extends StringToTParser{ $result->register("absorption", fn() => VanillaEffects::ABSORPTION()); $result->register("blindness", fn() => VanillaEffects::BLINDNESS()); $result->register("conduit_power", fn() => VanillaEffects::CONDUIT_POWER()); + $result->register("darkness", fn() => VanillaEffects::DARKNESS()); $result->register("fatal_poison", fn() => VanillaEffects::FATAL_POISON()); $result->register("fire_resistance", fn() => VanillaEffects::FIRE_RESISTANCE()); $result->register("haste", fn() => VanillaEffects::HASTE()); diff --git a/src/entity/effect/VanillaEffects.php b/src/entity/effect/VanillaEffects.php index 04f7985da..50544054a 100644 --- a/src/entity/effect/VanillaEffects.php +++ b/src/entity/effect/VanillaEffects.php @@ -36,6 +36,7 @@ use pocketmine\utils\RegistryTrait; * @method static AbsorptionEffect ABSORPTION() * @method static Effect BLINDNESS() * @method static Effect CONDUIT_POWER() + * @method static Effect DARKNESS() * @method static PoisonEffect FATAL_POISON() * @method static Effect FIRE_RESISTANCE() * @method static Effect HASTE() @@ -68,6 +69,7 @@ final class VanillaEffects{ //TODO: bad_omen self::register("blindness", new Effect(KnownTranslationFactory::potion_blindness(), new Color(0x1f, 0x1f, 0x23), true)); self::register("conduit_power", new Effect(KnownTranslationFactory::potion_conduitPower(), new Color(0x1d, 0xc2, 0xd1))); + self::register("darkness", new Effect(KnownTranslationFactory::effect_darkness(), new Color(0x29, 0x27, 0x21), true, 600, false)); self::register("fatal_poison", new PoisonEffect(KnownTranslationFactory::potion_poison(), new Color(0x4e, 0x93, 0x31), true, 600, true, true)); self::register("fire_resistance", new Effect(KnownTranslationFactory::potion_fireResistance(), new Color(0xe4, 0x9a, 0x3a))); self::register("haste", new Effect(KnownTranslationFactory::potion_digSpeed(), new Color(0xd9, 0xc0, 0x43))); From 83ddcce987bdc1b52256150cd42896ec82bca39d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 12 Nov 2022 16:57:35 +0000 Subject: [PATCH 39/53] World: Considerably simplify ticked chunk selection WARNING: This WILL have a performance impact on larger servers, but that's the price to pay for having crops actually grow. The old overengineered method for doing this was causing glacially slow crop growth, vanilla parity issues and really just didn't make much sense. This method is probably incorrect too, but it will at least produce visible results. --- resources/pocketmine.yml | 2 -- src/world/World.php | 30 +++++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/resources/pocketmine.yml b/resources/pocketmine.yml index 4ad8ea9c0..ac60afe53 100644 --- a/resources/pocketmine.yml +++ b/resources/pocketmine.yml @@ -119,8 +119,6 @@ chunk-sending: spawn-radius: 4 chunk-ticking: - #Max amount of chunks processed each tick - per-tick: 40 #Radius of chunks around a player to tick tick-radius: 3 #Number of blocks inside ticking areas' subchunks that get ticked every tick. Higher values will accelerate events diff --git a/src/world/World.php b/src/world/World.php index f820a787f..e9441e9bb 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -66,6 +66,7 @@ use pocketmine\network\mcpe\protocol\BlockActorDataPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; +use pocketmine\player\ChunkSelector; use pocketmine\player\Player; use pocketmine\promise\Promise; use pocketmine\promise\PromiseResolver; @@ -317,7 +318,6 @@ class World implements ChunkManager{ private int $sleepTicks = 0; private int $chunkTickRadius; - private int $chunksPerTick; private int $tickedBlocksPerSubchunkPerTick = self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK; /** * @var true[] @@ -493,7 +493,11 @@ class World implements ChunkManager{ $cfg = $this->server->getConfigGroup(); $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, $cfg->getPropertyInt("chunk-ticking.tick-radius", 4))); - $this->chunksPerTick = $cfg->getPropertyInt("chunk-ticking.per-tick", 40); + if($cfg->getPropertyInt("chunk-ticking.per-tick", 40) <= 0){ + //TODO: this needs l10n + $this->logger->warning("\"chunk-ticking.per-tick\" setting is deprecated, but you've used it to disable chunk ticking. Set \"chunk-ticking.tick-radius\" to 0 in \"pocketmine.yml\" instead."); + $this->chunkTickRadius = 0; + } $this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt("chunk-ticking.blocks-per-subchunk-per-tick", self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK); $this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt("chunk-generation.population-queue-size", 2); @@ -1101,7 +1105,7 @@ class World implements ChunkManager{ } private function tickChunks() : void{ - if($this->chunksPerTick <= 0 || count($this->tickingLoaders) === 0){ + if($this->chunkTickRadius <= 0 || count($this->tickingLoaders) === 0){ return; } @@ -1110,19 +1114,15 @@ class World implements ChunkManager{ /** @var bool[] $chunkTickList chunkhash => dummy */ $chunkTickList = []; - $chunksPerLoader = min(200, max(1, (int) ((($this->chunksPerTick - count($this->tickingLoaders)) / count($this->tickingLoaders)) + 0.5))); - $randRange = 3 + $chunksPerLoader / 30; - $randRange = (int) ($randRange > $this->chunkTickRadius ? $this->chunkTickRadius : $randRange); - + $selector = new ChunkSelector(); foreach($this->tickingLoaders as $loader){ - $chunkX = (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE; - $chunkZ = (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE; - - for($chunk = 0; $chunk < $chunksPerLoader; ++$chunk){ - $dx = mt_rand(-$randRange, $randRange); - $dz = mt_rand(-$randRange, $randRange); - $hash = World::chunkHash($dx + $chunkX, $dz + $chunkZ); - if(!isset($chunkTickList[$hash]) && isset($this->chunks[$hash]) && $this->isChunkTickable($dx + $chunkX, $dz + $chunkZ)){ + foreach($selector->selectChunks( + $this->chunkTickRadius, + (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE, + (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE + ) as $hash){ + World::getXZ($hash, $chunkX, $chunkZ); + if(!isset($chunkTickList[$hash]) && isset($this->chunks[$hash]) && $this->isChunkTickable($chunkX, $chunkZ)){ $chunkTickList[$hash] = true; } } From b4b8ef1c6b56608c7813cedbf8818a7ad829b49f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 12 Nov 2022 22:21:30 +0000 Subject: [PATCH 40/53] World: Improve performance of ticked chunk selection when lots of loaders use the same chunk since we use the same simulation distance for every loader, multiple loaders on the same chunk will produce the same set of ticked chunks. --- src/world/World.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index e9441e9bb..174281118 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1114,12 +1114,23 @@ class World implements ChunkManager{ /** @var bool[] $chunkTickList chunkhash => dummy */ $chunkTickList = []; + $centerChunks = []; + $selector = new ChunkSelector(); foreach($this->tickingLoaders as $loader){ + $centerChunkX = (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE; + $centerChunkZ = (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE; + $centerChunkPosHash = World::chunkHash($centerChunkX, $centerChunkZ); + if(isset($centerChunks[$centerChunkPosHash])){ + //we already queued chunks in this radius because of a previous loader on the same chunk + continue; + } + $centerChunks[$centerChunkPosHash] = true; + foreach($selector->selectChunks( $this->chunkTickRadius, - (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE, - (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE + $centerChunkX, + $centerChunkZ ) as $hash){ World::getXZ($hash, $chunkX, $chunkZ); if(!isset($chunkTickList[$hash]) && isset($this->chunks[$hash]) && $this->isChunkTickable($chunkX, $chunkZ)){ From 2fcff1357848f6e7a0f4a4ab487eaa5bffe2a3ae Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 12 Nov 2022 23:32:12 +0000 Subject: [PATCH 41/53] World: added getter and setter for simulation radius --- src/world/World.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/world/World.php b/src/world/World.php index 174281118..b41ba3bb4 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1104,6 +1104,21 @@ class World implements ChunkManager{ unset($this->randomTickBlocks[$block->getFullId()]); } + /** + * Returns the radius of chunks to be ticked around each ticking chunk loader (usually players). This is referred to + * as "simulation distance" in the Minecraft: Bedrock world options screen. + */ + public function getChunkTickRadius() : int{ + return $this->chunkTickRadius; + } + + /** + * Sets the radius of chunks ticked around each ticking chunk loader (usually players). + */ + public function setChunkTickRadius(int $radius) : void{ + $this->chunkTickRadius = $radius; + } + private function tickChunks() : void{ if($this->chunkTickRadius <= 0 || count($this->tickingLoaders) === 0){ return; From e5bc4deb1224c5422f7da595a9e444ca98663eeb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 13 Nov 2022 14:29:59 +0000 Subject: [PATCH 42/53] Release 4.11.0-BETA2 --- changelogs/4.11-beta.md | 25 +++++++++++++++++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/changelogs/4.11-beta.md b/changelogs/4.11-beta.md index 9da45f30c..0d8bc257c 100644 --- a/changelogs/4.11-beta.md +++ b/changelogs/4.11-beta.md @@ -65,3 +65,28 @@ Released 7th November 2022. - Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. - `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. - `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused. + +# 4.11.0-BETA2 +Released 13th November 2022. + +## Configuration +- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release. + - The functionality of this setting has been removed, since it caused more problems than it solved. + - Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead. + +## Gameplay +- Improved chunk random ticking: + - Removed the limit on chunks ticked per tick, and its associated config option is no longer respected. + - This change significantly improves crop and plant growth with large numbers of players, but may cause higher CPU usage. + - This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience. + - Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius. +- Implemented Darkness effect. + +## API +### `pocketmine\world` +- The following new API methods have been added: + - `public World->getChunkTickRadius() : int` - returns the world's simulation radius + - `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius + +## Internals +- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index cfc5ddb12..b50e510c0 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "4.11.0-BETA2"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "beta"; private function __construct(){ From beed6efd4e084a99a894c9a86c8845dd4afb49ea Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 13 Nov 2022 14:30:11 +0000 Subject: [PATCH 43/53] 4.11.0-BETA3 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b50e510c0..87c5deb7c 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.11.0-BETA2"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "4.11.0-BETA3"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "beta"; private function __construct(){ From ff2391e74aefe904599d762e7fa6867663de2706 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 15 Nov 2022 15:26:55 +0000 Subject: [PATCH 44/53] FormattedCommandAlias: use new string for description --- src/command/FormattedCommandAlias.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/FormattedCommandAlias.php b/src/command/FormattedCommandAlias.php index 6ec7b129a..df2b8f4fe 100644 --- a/src/command/FormattedCommandAlias.php +++ b/src/command/FormattedCommandAlias.php @@ -52,7 +52,7 @@ class FormattedCommandAlias extends Command{ string $alias, private array $formatStrings ){ - parent::__construct($alias); + parent::__construct($alias, KnownTranslationFactory::pocketmine_command_userDefined_description()); } public function execute(CommandSender $sender, string $commandLabel, array $args){ From d19f0bf7be9cca43cf75c921f8ce42daf80cc989 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 15 Nov 2022 15:27:14 +0000 Subject: [PATCH 45/53] Language: added getAll() --- src/lang/Language.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lang/Language.php b/src/lang/Language.php index 3c61a0cdd..57c5c78e1 100644 --- a/src/lang/Language.php +++ b/src/lang/Language.php @@ -173,6 +173,14 @@ class Language{ return $this->internalGet($id) ?? $id; } + /** + * @return string[] + * @phpstan-return array + */ + public function getAll() : array{ + return $this->lang; + } + protected function parseTranslation(string $text, ?string $onlyPrefix = null) : string{ $newString = ""; From cca22046ab8b51f7fa6d0a8932b29bb5bc913379 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 15 Nov 2022 20:21:39 +0000 Subject: [PATCH 46/53] Updated pocketmine/locale-data --- composer.json | 2 +- composer.lock | 14 +++++++------- src/lang/KnownTranslationFactory.php | 12 ++++++++++++ src/lang/KnownTranslationKeys.php | 3 +++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index b5e172239..377e1825e 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/classloader": "^0.2.0", "pocketmine/color": "^0.2.0", "pocketmine/errorhandler": "^0.6.0", - "pocketmine/locale-data": "~2.9.0", + "pocketmine/locale-data": "~2.10.0", "pocketmine/log": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0", "pocketmine/math": "^0.4.0", diff --git a/composer.lock b/composer.lock index 60909f836..45ec89347 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b535dc262141936698680b92902f8e28", + "content-hash": "87c9893eaf1fec7a05735fab6bb97f17", "packages": [ { "name": "adhocore/json-comment", @@ -536,16 +536,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.9.2", + "version": "2.10.2", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "8813ffd2a4501521ca9433c534f3009f941de136" + "reference": "c2b8b63a85269304409406922181dd5438bcaff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/8813ffd2a4501521ca9433c534f3009f941de136", - "reference": "8813ffd2a4501521ca9433c534f3009f941de136", + "url": "https://api.github.com/repos/pmmp/Language/zipball/c2b8b63a85269304409406922181dd5438bcaff2", + "reference": "c2b8b63a85269304409406922181dd5438bcaff2", "shasum": "" }, "type": "library", @@ -553,9 +553,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.9.2" + "source": "https://github.com/pmmp/Language/tree/2.10.2" }, - "time": "2022-10-21T20:30:38+00:00" + "time": "2022-11-15T13:55:05+00:00" }, { "name": "pocketmine/log", diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 577c125fa..64dcf0305 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -863,6 +863,10 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ENCHANTMENT_SOUL_SPEED, []); } + public static function enchantment_swift_sneak() : Translatable{ + return new Translatable(KnownTranslationKeys::ENCHANTMENT_SWIFT_SNEAK, []); + } + public static function enchantment_thorns() : Translatable{ return new Translatable(KnownTranslationKeys::ENCHANTMENT_THORNS, []); } @@ -954,6 +958,10 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_13_DESC, []); } + public static function item_record_5_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_5_DESC, []); + } + public static function item_record_blocks_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_BLOCKS_DESC, []); } @@ -978,6 +986,10 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_MELLOHI_DESC, []); } + public static function item_record_otherside_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_OTHERSIDE_DESC, []); + } + public static function item_record_pigstep_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 87f88515d..82a060223 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -190,6 +190,7 @@ final class KnownTranslationKeys{ public const ENCHANTMENT_PROTECT_FIRE = "enchantment.protect.fire"; public const ENCHANTMENT_PROTECT_PROJECTILE = "enchantment.protect.projectile"; public const ENCHANTMENT_SOUL_SPEED = "enchantment.soul_speed"; + public const ENCHANTMENT_SWIFT_SNEAK = "enchantment.swift_sneak"; public const ENCHANTMENT_THORNS = "enchantment.thorns"; public const ENCHANTMENT_TRIDENTCHANNELING = "enchantment.tridentChanneling"; public const ENCHANTMENT_TRIDENTIMPALING = "enchantment.tridentImpaling"; @@ -211,12 +212,14 @@ final class KnownTranslationKeys{ public const IP_WARNING = "ip_warning"; public const ITEM_RECORD_11_DESC = "item.record_11.desc"; public const ITEM_RECORD_13_DESC = "item.record_13.desc"; + public const ITEM_RECORD_5_DESC = "item.record_5.desc"; public const ITEM_RECORD_BLOCKS_DESC = "item.record_blocks.desc"; public const ITEM_RECORD_CAT_DESC = "item.record_cat.desc"; public const ITEM_RECORD_CHIRP_DESC = "item.record_chirp.desc"; public const ITEM_RECORD_FAR_DESC = "item.record_far.desc"; public const ITEM_RECORD_MALL_DESC = "item.record_mall.desc"; public const ITEM_RECORD_MELLOHI_DESC = "item.record_mellohi.desc"; + public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.desc"; public const ITEM_RECORD_PIGSTEP_DESC = "item.record_pigstep.desc"; public const ITEM_RECORD_STAL_DESC = "item.record_stal.desc"; public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc"; From 36ab34df294ce803e818cf5d35340d7dd6a8b7ba Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Wed, 23 Nov 2022 13:57:26 +0100 Subject: [PATCH 47/53] Implement BlockDeathEvent (#5420) closes #4711 --- src/block/BaseCoral.php | 7 ++++++- src/block/CoralBlock.php | 7 ++++++- src/event/block/BlockDeathEvent.php | 32 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/event/block/BlockDeathEvent.php diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index f3bb9255d..0912a85ba 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; +use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; abstract class BaseCoral extends Transparent{ @@ -50,7 +51,11 @@ abstract class BaseCoral extends Transparent{ //TODO: check water inside the block itself (not supported on the API yet) if(!$hasWater){ - $world->setBlock($this->position, $this->setDead(true)); + $ev = new BlockDeathEvent($this, $this->setDead(true)); + $ev->call(); + if(!$ev->isCancelled()){ + $world->setBlock($this->position, $ev->getNewState()); + } } } } diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index e29591b3d..0718112eb 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\InvalidBlockStateException; use pocketmine\data\bedrock\CoralTypeIdMap; +use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -77,7 +78,11 @@ final class CoralBlock extends Opaque{ } } if(!$hasWater){ - $world->setBlock($this->position, $this->setDead(true)); + $ev = new BlockDeathEvent($this, $this->setDead(true)); + $ev->call(); + if(!$ev->isCancelled()){ + $world->setBlock($this->position, $ev->getNewState()); + } } } } diff --git a/src/event/block/BlockDeathEvent.php b/src/event/block/BlockDeathEvent.php new file mode 100644 index 000000000..c07e3ad6e --- /dev/null +++ b/src/event/block/BlockDeathEvent.php @@ -0,0 +1,32 @@ + Date: Fri, 25 Nov 2022 15:28:41 +0100 Subject: [PATCH 48/53] Fix missing sounds when interacting with item frames (#5383) closes #5168 --- src/block/ItemFrame.php | 8 +++++ src/world/sound/ItemFrameAddItemSound.php | 35 ++++++++++++++++++++ src/world/sound/ItemFrameRemoveItemSound.php | 35 ++++++++++++++++++++ src/world/sound/ItemFrameRotateItemSound.php | 35 ++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/world/sound/ItemFrameAddItemSound.php create mode 100644 src/world/sound/ItemFrameRemoveItemSound.php create mode 100644 src/world/sound/ItemFrameRotateItemSound.php diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index d71621943..8df62c72f 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -31,6 +31,9 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; +use pocketmine\world\sound\ItemFrameAddItemSound; +use pocketmine\world\sound\ItemFrameRemoveItemSound; +use pocketmine\world\sound\ItemFrameRotateItemSound; use function is_infinite; use function is_nan; use function lcg_value; @@ -136,8 +139,12 @@ class ItemFrame extends Flowable{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->framedItem !== null){ $this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS; + + $this->position->getWorld()->addSound($this->position, new ItemFrameRotateItemSound()); }elseif(!$item->isNull()){ $this->framedItem = $item->pop(); + + $this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound()); }else{ return true; } @@ -154,6 +161,7 @@ class ItemFrame extends Flowable{ $world = $this->position->getWorld(); if(lcg_value() <= $this->itemDropChance){ $world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem); + $world->addSound($this->position, new ItemFrameRemoveItemSound()); } $this->setFramedItem(null); $world->setBlock($this->position, $this); diff --git a/src/world/sound/ItemFrameAddItemSound.php b/src/world/sound/ItemFrameAddItemSound.php new file mode 100644 index 000000000..22e35aead --- /dev/null +++ b/src/world/sound/ItemFrameAddItemSound.php @@ -0,0 +1,35 @@ + Date: Fri, 25 Nov 2022 14:37:37 +0000 Subject: [PATCH 49/53] Update BedrockProtocol --- composer.json | 2 +- composer.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 377e1825e..09d33c80b 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "fgrosse/phpasn1": "^2.3", "netresearch/jsonmapper": "^4.0", "pocketmine/bedrock-data": "~1.12.0+bedrock-1.19.40", - "pocketmine/bedrock-protocol": "~14.0.0+bedrock-1.19.40", + "pocketmine/bedrock-protocol": "~16.0.0+bedrock-1.19.40", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/classloader": "^0.2.0", diff --git a/composer.lock b/composer.lock index 45ec89347..b8f472c5c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87c9893eaf1fec7a05735fab6bb97f17", + "content-hash": "b1d1d3a85ef8a8aec19b53f80d10e189", "packages": [ { "name": "adhocore/json-comment", @@ -275,16 +275,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "14.0.0+bedrock-1.19.40", + "version": "16.0.0+bedrock-1.19.40", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "b455a742779fee94d25f931cc2cbf6b2c5d61c1f" + "reference": "ce900ffa6a4cc07af92686f27d580dd2e2541382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/b455a742779fee94d25f931cc2cbf6b2c5d61c1f", - "reference": "b455a742779fee94d25f931cc2cbf6b2c5d61c1f", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ce900ffa6a4cc07af92686f27d580dd2e2541382", + "reference": "ce900ffa6a4cc07af92686f27d580dd2e2541382", "shasum": "" }, "require": { @@ -298,7 +298,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.8.8", + "phpstan/phpstan": "1.9.0", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5" @@ -316,9 +316,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.19.40" + "source": "https://github.com/pmmp/BedrockProtocol/tree/16.0.0+bedrock-1.19.40" }, - "time": "2022-10-25T21:51:46+00:00" + "time": "2022-11-19T16:11:48+00:00" }, { "name": "pocketmine/binaryutils", From 642630a4d2d95211620b2ce543bee97ff58c576a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Nov 2022 14:57:47 +0000 Subject: [PATCH 50/53] ... --- composer.lock | 106 +++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/composer.lock b/composer.lock index b8f472c5c..fe777d489 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b1d1d3a85ef8a8aec19b53f80d10e189", + "content-hash": "c471cf28062595a981064058c8b12dbb", "packages": [ { "name": "adhocore/json-comment", @@ -536,16 +536,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.10.2", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "c2b8b63a85269304409406922181dd5438bcaff2" + "reference": "d641577c8b15d9e60750dd681c0d684b89ae55e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/c2b8b63a85269304409406922181dd5438bcaff2", - "reference": "c2b8b63a85269304409406922181dd5438bcaff2", + "url": "https://api.github.com/repos/pmmp/Language/zipball/d641577c8b15d9e60750dd681c0d684b89ae55e1", + "reference": "d641577c8b15d9e60750dd681c0d684b89ae55e1", "shasum": "" }, "type": "library", @@ -553,9 +553,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.10.2" + "source": "https://github.com/pmmp/Language/tree/2.10.3" }, - "time": "2022-11-15T13:55:05+00:00" + "time": "2022-11-25T14:21:36+00:00" }, { "name": "pocketmine/log", @@ -1086,16 +1086,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -1110,7 +1110,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1148,7 +1148,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -1164,20 +1164,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -1192,7 +1192,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1231,7 +1231,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -1247,20 +1247,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -1269,7 +1269,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1314,7 +1314,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -1330,20 +1330,20 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { @@ -1352,7 +1352,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1393,7 +1393,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -1409,7 +1409,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "webmozart/assert", @@ -1653,16 +1653,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.1", + "version": "v4.15.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", "shasum": "" }, "require": { @@ -1703,9 +1703,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" }, - "time": "2022-09-04T07:30:47+00:00" + "time": "2022-11-12T15:38:23+00:00" }, { "name": "phar-io/manifest", @@ -1820,16 +1820,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.9.1", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f" + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa", "shasum": "" }, "require": { @@ -1859,7 +1859,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.1" + "source": "https://github.com/phpstan/phpstan/tree/1.9.2" }, "funding": [ { @@ -1875,7 +1875,7 @@ "type": "tidelift" } ], - "time": "2022-11-04T13:35:59+00:00" + "time": "2022-11-10T09:56:11+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1979,16 +1979,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.18", + "version": "9.2.19", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a" + "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/12fddc491826940cf9b7e88ad9664cf51f0f6d0a", - "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559", + "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559", "shasum": "" }, "require": { @@ -2044,7 +2044,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.18" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19" }, "funding": [ { @@ -2052,7 +2052,7 @@ "type": "github" } ], - "time": "2022-10-27T13:35:33+00:00" + "time": "2022-11-18T07:47:47+00:00" }, { "name": "phpunit/php-file-iterator", From 641d35a30f032c4a15ffd13fd0b4de415988b6f9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Nov 2022 14:59:58 +0000 Subject: [PATCH 51/53] Updated pocketmine/locale-data to get new translations --- composer.json | 2 +- composer.lock | 14 +++++----- src/lang/KnownTranslationFactory.php | 41 ++++++++++++++++++++++++++++ src/lang/KnownTranslationKeys.php | 9 ++++++ 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index e365b3fbe..e47286f4a 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/classloader": "^0.2.0", "pocketmine/color": "^0.2.0", "pocketmine/errorhandler": "^0.6.0", - "pocketmine/locale-data": "~2.10.0", + "pocketmine/locale-data": "~2.11.0", "pocketmine/log": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0", "pocketmine/math": "^0.4.0", diff --git a/composer.lock b/composer.lock index fe777d489..6843484d2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c471cf28062595a981064058c8b12dbb", + "content-hash": "ff2bc73e9b0acccb1e63ddef2412fe31", "packages": [ { "name": "adhocore/json-comment", @@ -536,16 +536,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.10.3", + "version": "2.11.0", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "d641577c8b15d9e60750dd681c0d684b89ae55e1" + "reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/d641577c8b15d9e60750dd681c0d684b89ae55e1", - "reference": "d641577c8b15d9e60750dd681c0d684b89ae55e1", + "url": "https://api.github.com/repos/pmmp/Language/zipball/4b33d8fa53eda53d9662a7478806ebae2e4a5c53", + "reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53", "shasum": "" }, "type": "library", @@ -553,9 +553,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.10.3" + "source": "https://github.com/pmmp/Language/tree/2.11.0" }, - "time": "2022-11-25T14:21:36+00:00" + "time": "2022-11-25T14:24:34+00:00" }, { "name": "pocketmine/log", diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 64dcf0305..f1db1c6d1 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -43,6 +43,34 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ACCEPT_LICENSE, []); } + public static function action_interact_armorstand_equip() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_ARMORSTAND_EQUIP, []); + } + + public static function action_interact_armorstand_pose() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_ARMORSTAND_POSE, []); + } + + public static function action_interact_exit_boat() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_EXIT_BOAT, []); + } + + public static function action_interact_fishing() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_FISHING, []); + } + + public static function action_interact_name() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_NAME, []); + } + + public static function action_interact_ride_boat() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_RIDE_BOAT, []); + } + + public static function action_interact_ride_minecart() : Translatable{ + return new Translatable(KnownTranslationKeys::ACTION_INTERACT_RIDE_MINECART, []); + } + public static function chat_type_achievement(Translatable|string $param0, Translatable|string $param1) : Translatable{ return new Translatable(KnownTranslationKeys::CHAT_TYPE_ACHIEVEMENT, [ 0 => $param0, @@ -627,6 +655,12 @@ final class KnownTranslationFactory{ ]); } + public static function death_attack_fallingBlock(Translatable|string $param0) : Translatable{ + return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FALLINGBLOCK, [ + 0 => $param0, + ]); + } + public static function death_attack_generic(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [ 0 => $param0, @@ -691,6 +725,13 @@ final class KnownTranslationFactory{ ]); } + public static function death_attack_trident(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::DEATH_ATTACK_TRIDENT, [ + 0 => $param0, + 1 => $param1, + ]); + } + public static function death_attack_wither(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_WITHER, [ 0 => $param0, diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 82a060223..bd98496cb 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -33,6 +33,13 @@ final class KnownTranslationKeys{ public const ABILITY_FLIGHT = "ability.flight"; public const ABILITY_NOCLIP = "ability.noclip"; public const ACCEPT_LICENSE = "accept_license"; + public const ACTION_INTERACT_ARMORSTAND_EQUIP = "action.interact.armorstand.equip"; + public const ACTION_INTERACT_ARMORSTAND_POSE = "action.interact.armorstand.pose"; + public const ACTION_INTERACT_EXIT_BOAT = "action.interact.exit.boat"; + public const ACTION_INTERACT_FISHING = "action.interact.fishing"; + public const ACTION_INTERACT_NAME = "action.interact.name"; + public const ACTION_INTERACT_RIDE_BOAT = "action.interact.ride.boat"; + public const ACTION_INTERACT_RIDE_MINECART = "action.interact.ride.minecart"; public const CHAT_TYPE_ACHIEVEMENT = "chat.type.achievement"; public const CHAT_TYPE_ADMIN = "chat.type.admin"; public const CHAT_TYPE_ANNOUNCEMENT = "chat.type.announcement"; @@ -138,6 +145,7 @@ final class KnownTranslationKeys{ public const DEATH_ATTACK_EXPLOSION = "death.attack.explosion"; public const DEATH_ATTACK_EXPLOSION_PLAYER = "death.attack.explosion.player"; public const DEATH_ATTACK_FALL = "death.attack.fall"; + public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock"; public const DEATH_ATTACK_GENERIC = "death.attack.generic"; public const DEATH_ATTACK_INFIRE = "death.attack.inFire"; public const DEATH_ATTACK_INWALL = "death.attack.inWall"; @@ -148,6 +156,7 @@ final class KnownTranslationKeys{ public const DEATH_ATTACK_OUTOFWORLD = "death.attack.outOfWorld"; public const DEATH_ATTACK_PLAYER = "death.attack.player"; public const DEATH_ATTACK_PLAYER_ITEM = "death.attack.player.item"; + public const DEATH_ATTACK_TRIDENT = "death.attack.trident"; public const DEATH_ATTACK_WITHER = "death.attack.wither"; public const DEATH_FELL_ACCIDENT_GENERIC = "death.fell.accident.generic"; public const DEFAULT_GAMEMODE = "default_gamemode"; From d450264e1c39077357b240b80688238a8187a758 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Nov 2022 20:29:46 +0000 Subject: [PATCH 52/53] Release 4.11.0 --- changelogs/4.11.md | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 6 +-- 2 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 changelogs/4.11.md diff --git a/changelogs/4.11.md b/changelogs/4.11.md new file mode 100644 index 000000000..04de9824d --- /dev/null +++ b/changelogs/4.11.md @@ -0,0 +1,102 @@ +**For Minecraft: Bedrock Edition 1.19.40** + +This is a minor feature release for PocketMine-MP, introducing some new features and improvements. + +### Note about API versions +Plugins which don't touch the protocol and 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 protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 4.11.0 +Released 25th November 2022. + +## General +- Packet receive timings have now been split into two subcategories - Decode and Handle. +- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`. + - Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway. + - Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess. +- Console command output now appears on the terminal only, and is not written to the log file. +- The output from console commands now appears with a `Command output |` prefix, instead of as a log message. +- User-defined `pocketmine.yml` custom commands now use a generic description which makes clear the command is config-defined. +- Introduced validation for the `--data` and `--plugins` command line options. +- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder. + - e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key` + - The file must contain the raw key bytes, and must not end with a newline. + +## Configuration +- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release. + - The functionality of this setting has been removed, since it caused more problems than it solved. + - Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead. + +## Gameplay +- Fixed supporting blocks of dead bush to be in line with vanilla. +- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block. +- Fixed missing sounds when adding, rotating, or removing items in item frames. +- Fixed modifier values for Instant Damage and Regeneration effects. +- Implemented Darkness effect. +- Improved chunk random ticking: + - Removed the limit on chunks ticked per tick, and its associated config option is no longer respected. + - This change significantly improves crop and plant growth with large numbers of players. + - This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience. + - **Warning: This change will result in increased CPU usage if players are spread over a very large area.** + - Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius. + +## API +### General +- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`). +- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5. + - To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release. + +### `pocketmine` +- The following API methods are now deprecated: + - `Server->getPlayerByPrefix()` + +### `pocketmine\entity` +- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created. +- The following API methods are now deprecated: + - `Living->hasLineOfSight()` + +### `pocketmine\event\block` +- The following new classes have been added: + - `BlockDeathEvent` - event called when coral or coral blocks die due to lack of water + +### `pocketmine\item` +- The following new API methods have been added: + - `public Armor->clearCustomColor() : $this` + +### `pocketmine\inventory\transaction` +- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s. + +### `pocketmine\lang` +- The following new API methods have been added: + - `public Language->getAll() : array` + +### `pocketmine\player` +- The following new API methods have been added: + - `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message + +### `pocketmine\utils` +- The following new API methods have been added: + - `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code + +### `pocketmine\world` +- The following new API methods have been added: + - `public World->getChunkTickRadius() : int` - returns the world's simulation radius + - `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius + +### `pocketmine\world\sound` +- The following new classes have been added: + - `ItemFrameAddItemSound` + - `ItemFrameRemoveItemSound` + - `ItemFrameRotateItemSound` + +## Internals +- Improved performance of `ContainerTrait` dropping items on block destroy. (24e72ec109c1442b09558df89b6833cf2f2e0ec7) +- Avoid repeated calls to `Position->getWorld()` (use local variables). (2940547026db40ce76deb46e992870de3ead79ad) +- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. (e90abecf38d9c57635fa0497514bba7e546a2469) +- Console polling is now done on the main thread (no longer a performance concern). +- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. +- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. +- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused. +- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions. \ No newline at end of file diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 87c5deb7c..8c460aa01 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,9 +31,9 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.11.0-BETA3"; - public const IS_DEVELOPMENT_BUILD = true; - public const BUILD_CHANNEL = "beta"; + public const BASE_VERSION = "4.11.0"; + public const IS_DEVELOPMENT_BUILD = false; + public const BUILD_CHANNEL = "stable"; private function __construct(){ //NOOP From ec9f9a469ff56ca43362fb3c751246421c936526 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Nov 2022 20:29:49 +0000 Subject: [PATCH 53/53] 4.11.1 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 8c460aa01..1c93eb558 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.11.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "4.11.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; private function __construct(){