Merge branch 'stable' into next-minor

This commit is contained in:
Dylan K. Taylor 2022-07-24 21:21:37 +01:00
commit 16ed16722a
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
11 changed files with 136 additions and 82 deletions

View File

@ -46,7 +46,7 @@ jobs:
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}") run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
- name: Build image for tag - name: Build image for tag
uses: docker/build-push-action@v3.0.0 uses: docker/build-push-action@v3.1.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -59,7 +59,7 @@ jobs:
- name: Build image for major tag - name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0 uses: docker/build-push-action@v3.1.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -72,7 +72,7 @@ jobs:
- name: Build image for minor tag - name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0 uses: docker/build-push-action@v3.1.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp
@ -85,7 +85,7 @@ jobs:
- name: Build image for latest tag - name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable' if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v3.0.0 uses: docker/build-push-action@v3.1.0
with: with:
push: true push: true
context: ./pocketmine-mp context: ./pocketmine-mp

View File

@ -18,7 +18,7 @@ jobs:
submodules: true submodules: true
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@2.20.0 uses: shivammathur/setup-php@2.21.0
with: with:
php-version: 8.0 php-version: 8.0

View File

@ -195,7 +195,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup PHP and tools - name: Setup PHP and tools
uses: shivammathur/setup-php@2.20.0 uses: shivammathur/setup-php@2.21.0
with: with:
php-version: 8.0 php-version: 8.0
tools: php-cs-fixer:3.2 tools: php-cs-fixer:3.2

@ -1 +1 @@
Subproject commit 11103498ca761be83598f9759cc7196c167fcb7e Subproject commit f292501a703352ab793b07b7861f3e1b3860ed86

View File

@ -12,3 +12,20 @@ Released 13th July 2022.
## General ## General
- Added support for Minecraft: Bedrock Edition 1.19.10. - Added support for Minecraft: Bedrock Edition 1.19.10.
- Removed support for older versions. - Removed support for older versions.
# 4.6.1
Released 22nd July 2022.
## Tools
- `build/generate-registry-annotations.php` now supports processing single files (useful for PhpStorm file watchers).
## API
- Updated documentation for `AsyncTask`.
## Fixes
- Fixed incorrect items being displayed in item frames.
- Fixed books not showing in lecterns.
- Fixed incorrect damage interval of Wither status effect.
- Fixed incorrect fire ticks when being set on fire by lava (8 seconds in Bedrock instead of 15).
- `Entity->attack()` now cancels damage from `FIRE` and `FIRE_TICK` damage causes if the entity is fireproof.
- Fixed inventory windows getting force-closed when the client attempts to use an enchanting table or anvil.

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{ final class VersionInfo{
public const NAME = "PocketMine-MP"; public const NAME = "PocketMine-MP";
public const BASE_VERSION = "4.6.1"; public const BASE_VERSION = "4.6.2";
public const IS_DEVELOPMENT_BUILD = true; public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "beta"; public const BUILD_CHANNEL = "beta";

View File

@ -50,6 +50,7 @@ use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DeviceOS; use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -57,12 +58,15 @@ use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\UpdateAbilitiesPacketLayer;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\Limits; use pocketmine\utils\Limits;
use pocketmine\world\sound\TotemUseSound; use pocketmine\world\sound\TotemUseSound;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface; use Ramsey\Uuid\UuidInterface;
use function array_fill;
use function array_filter; use function array_filter;
use function array_key_exists; use function array_key_exists;
use function array_merge; use function array_merge;
@ -481,7 +485,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
GameMode::SURVIVAL, GameMode::SURVIVAL,
$this->getAllNetworkData(), $this->getAllNetworkData(),
UpdateAbilitiesPacket::create(0, 0, $this->getId() /* TODO: this should be unique ID */, []), UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new UpdateAbilitiesPacketLayer(
UpdateAbilitiesPacketLayer::LAYER_BASE,
array_fill(0, UpdateAbilitiesPacketLayer::NUMBER_OF_ABILITIES, false),
0.0,
0.0
)
]),
[], //TODO: entity links [], //TODO: entity links
"", //device ID (we intentionally don't send this - secvuln) "", //device ID (we intentionally don't send this - secvuln)
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln) DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)

View File

@ -145,6 +145,11 @@ class InGamePacketHandler extends PacketHandler{
/** @var UseItemTransactionData|null */ /** @var UseItemTransactionData|null */
protected $lastRightClickData = null; protected $lastRightClickData = null;
protected ?Vector3 $lastPlayerAuthInputPosition = null;
protected ?float $lastPlayerAuthInputYaw = null;
protected ?float $lastPlayerAuthInputPitch = null;
protected ?int $lastPlayerAuthInputFlags = null;
/** @var bool */ /** @var bool */
public $forceMoveSync = false; public $forceMoveSync = false;
@ -168,9 +173,10 @@ class InGamePacketHandler extends PacketHandler{
return true; return true;
} }
private function resolveOnOffInputFlags(PlayerAuthInputPacket $packet, int $startFlag, int $stopFlag) : ?bool{ private function resolveOnOffInputFlags(int $inputFlags, int $startFlag, int $stopFlag) : ?bool{
$enabled = $packet->hasFlag($startFlag); $enabled = ($inputFlags & (1 << $startFlag)) !== 0;
if($enabled !== $packet->hasFlag($stopFlag)){ $disabled = ($inputFlags & (1 << $stopFlag)) !== 0;
if($enabled !== $disabled){
return $enabled; return $enabled;
} }
//neither flag was set, or both were set //neither flag was set, or both were set
@ -179,25 +185,35 @@ class InGamePacketHandler extends PacketHandler{
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{ public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
$rawPos = $packet->getPosition(); $rawPos = $packet->getPosition();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->getYaw(), $packet->getHeadYaw(), $packet->getPitch()] as $float){ $rawYaw = $packet->getYaw();
$rawPitch = $packet->getPitch();
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $rawYaw, $packet->getHeadYaw(), $rawPitch] as $float){
if(is_infinite($float) || is_nan($float)){ if(is_infinite($float) || is_nan($float)){
$this->session->getLogger()->debug("Invalid movement received, contains NAN/INF components"); $this->session->getLogger()->debug("Invalid movement received, contains NAN/INF components");
return false; return false;
} }
} }
$yaw = fmod($packet->getYaw(), 360); if($rawYaw !== $this->lastPlayerAuthInputYaw || $rawPitch !== $this->lastPlayerAuthInputPitch){
$pitch = fmod($packet->getPitch(), 360); $this->lastPlayerAuthInputYaw = $rawYaw;
$this->lastPlayerAuthInputPitch = $rawPitch;
$yaw = fmod($rawYaw, 360);
$pitch = fmod($rawPitch, 360);
if($yaw < 0){ if($yaw < 0){
$yaw += 360; $yaw += 360;
} }
$this->player->setRotation($yaw, $pitch); $this->player->setRotation($yaw, $pitch);
}
$curPos = $this->player->getLocation(); $hasMoved = $this->lastPlayerAuthInputPosition === null || !$this->lastPlayerAuthInputPosition->equals($rawPos);
$newPos = $rawPos->round(4)->subtract(0, 1.62, 0); $newPos = $rawPos->round(4)->subtract(0, 1.62, 0);
if($this->forceMoveSync && $newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks if($this->forceMoveSync && $hasMoved){
$curPos = $this->player->getLocation();
if($newPos->distanceSquared($curPos) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->session->getLogger()->debug("Got outdated pre-teleport movement, received " . $newPos . ", expected " . $curPos); $this->session->getLogger()->debug("Got outdated pre-teleport movement, received " . $newPos . ", expected " . $curPos);
//Still getting movements from before teleport, ignore them //Still getting movements from before teleport, ignore them
return false; return false;
@ -205,11 +221,16 @@ class InGamePacketHandler extends PacketHandler{
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock // Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
$this->forceMoveSync = false; $this->forceMoveSync = false;
}
$sneaking = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING); $inputFlags = $packet->getInputFlags();
$sprinting = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING); if($inputFlags !== $this->lastPlayerAuthInputFlags){
$swimming = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING); $this->lastPlayerAuthInputFlags = $inputFlags;
$gliding = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$sneaking = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
$sprinting = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$mismatch = $mismatch =
($sneaking !== null && !$this->player->toggleSneak($sneaking)) | ($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
($sprinting !== null && !$this->player->toggleSprint($sprinting)) | ($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
@ -222,8 +243,10 @@ class InGamePacketHandler extends PacketHandler{
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){ if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
$this->player->jump(); $this->player->jump();
} }
}
if(!$this->forceMoveSync){ if(!$this->forceMoveSync && $hasMoved){
$this->lastPlayerAuthInputPosition = $rawPos;
//TODO: this packet has WAYYYYY more useful information that we're not using //TODO: this packet has WAYYYYY more useful information that we're not using
$this->player->handleMovement($newPos); $this->player->handleMovement($newPos);
} }

View File

@ -1180,7 +1180,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
return; return;
} }
$oldPos = $this->getLocation(); $oldPos = $this->location;
$distanceSquared = $newPos->distanceSquared($oldPos); $distanceSquared = $newPos->distanceSquared($oldPos);
$revert = false; $revert = false;
@ -1198,7 +1198,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
* asking for help if you suffer the consequences of messing with this. * asking for help if you suffer the consequences of messing with this.
*/ */
$this->logger->debug("Moved too fast, reverting movement"); $this->logger->debug("Moved too fast, reverting movement");
$this->logger->debug("Old position: " . $this->location->asVector3() . ", new position: " . $newPos); $this->logger->debug("Old position: " . $oldPos->asVector3() . ", new position: " . $newPos);
$revert = true; $revert = true;
}elseif(!$this->getWorld()->isInLoadedTerrain($newPos)){ }elseif(!$this->getWorld()->isInLoadedTerrain($newPos)){
$revert = true; $revert = true;
@ -1206,9 +1206,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
} }
if(!$revert && $distanceSquared != 0){ if(!$revert && $distanceSquared != 0){
$dx = $newPos->x - $this->location->x; $dx = $newPos->x - $oldPos->x;
$dy = $newPos->y - $this->location->y; $dy = $newPos->y - $oldPos->y;
$dz = $newPos->z - $this->location->z; $dz = $newPos->z - $oldPos->z;
$this->move($dx, $dy, $dz); $this->move($dx, $dy, $dz);
} }

View File

@ -33,19 +33,31 @@ use function spl_object_id;
/** /**
* Class used to run async tasks in other threads. * Class used to run async tasks in other threads.
* *
* An AsyncTask does not have its own thread. It is queued into an AsyncPool and executed if there is an async worker * An AsyncTask is run by a thread pool of reusable threads, and doesn't have its own dedicated thread. A thread is
* with no AsyncTask running. Therefore, an AsyncTask SHOULD NOT execute for more than a few seconds. For tasks that * usually chosen from the pool at random to run the task (though a specific thread in the pool may be selected
* run for a long time or infinitely, start another thread instead. * manually, if needed).
* Reusing threads this way has a much lower performance cost than starting an entirely new thread for every operation.
* AsyncTasks are therefore suitable for brief CPU-bound tasks, such as world generation, compression/decompression of
* data, etc.
*
* AsyncTask SHOULD NOT be used for I/O-bound tasks, such as network I/O, file I/O, database I/O, etc. The server's
* central AsyncPool is used for things like compressing network packets for sending, so using AsyncTask for I/O will
* slow the whole server down, stall chunk loading, etc.
*
* An AsyncTask SHOULD NOT run for more than a few seconds. For tasks that run for a long time or indefinitely, create
* a dedicated thread instead.
*
* The Server instance is not accessible inside {@link AsyncTask::onRun()}. It can only be accessed in the main server
* thread, e.g. during {@link AsyncTask::onCompletion()} or {@link AsyncTask::onProgressUpdate()}. This means that
* whatever you do in onRun() must be able to work without the Server instance.
* *
* WARNING: Any non-Threaded objects WILL BE SERIALIZED when assigned to members of AsyncTasks or other Threaded object. * WARNING: Any non-Threaded objects WILL BE SERIALIZED when assigned to members of AsyncTasks or other Threaded object.
* If later accessed from said Threaded object, you will be operating on a COPY OF THE OBJECT, NOT THE ORIGINAL OBJECT. * If later accessed from said Threaded object, you will be operating on a COPY OF THE OBJECT, NOT THE ORIGINAL OBJECT.
* If you want to store non-serializable objects to access when the task completes, store them using * If you want to store non-serializable objects to access when the task completes, store them using
* {@link AsyncTask::storeLocal}. * {@link AsyncTask::storeLocal}.
* *
* WARNING: As of pthreads v3.1.6, arrays are converted to Volatile objects when assigned as members of Threaded objects. * WARNING: Arrays are converted to Volatile objects when assigned as members of Threaded objects.
* Keep this in mind when using arrays stored as members of your AsyncTask. * Keep this in mind when using arrays stored as members of your AsyncTask.
*
* WARNING: Do not call PocketMine-MP API methods from other Threads!!
*/ */
abstract class AsyncTask extends \Threaded{ abstract class AsyncTask extends \Threaded{
/** /**

View File

@ -23,25 +23,20 @@ declare(strict_types=1);
namespace pocketmine\world\particle; namespace pocketmine\world\particle;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\Skin;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\convert\SkinAdapterSingleton; use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\types\DeviceOS; use pocketmine\network\mcpe\protocol\types\entity\ByteMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty; use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\IntMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty; use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use Ramsey\Uuid\Uuid;
use function str_repeat;
class FloatingTextParticle implements Particle{ class FloatingTextParticle implements Particle{
//TODO: HACK! //TODO: HACK!
@ -94,38 +89,34 @@ class FloatingTextParticle implements Particle{
} }
if(!$this->invisible){ if(!$this->invisible){
$uuid = Uuid::uuid4();
$name = $this->title . ($this->text !== "" ? "\n" . $this->text : ""); $name = $this->title . ($this->text !== "" ? "\n" . $this->text : "");
$p[] = PlayerListPacket::add([PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin("Standard_Custom", str_repeat("\x00", 8192))))]);
$actorFlags = ( $actorFlags = (
1 << EntityMetadataFlags::IMMOBILE 1 << EntityMetadataFlags::IMMOBILE
); );
$actorMetadata = [ $actorMetadata = [
EntityMetadataProperties::FLAGS => new LongMetadataProperty($actorFlags), EntityMetadataProperties::FLAGS => new LongMetadataProperty($actorFlags),
EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01) //zero causes problems on debug builds EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01), //zero causes problems on debug builds
EntityMetadataProperties::BOUNDING_BOX_WIDTH => new FloatMetadataProperty(0.0),
EntityMetadataProperties::BOUNDING_BOX_HEIGHT => new FloatMetadataProperty(0.0),
EntityMetadataProperties::NAMETAG => new StringMetadataProperty($name),
EntityMetadataProperties::VARIANT => new IntMetadataProperty(RuntimeBlockMapping::getInstance()->toRuntimeId(VanillaBlocks::AIR()->getFullId())),
EntityMetadataProperties::ALWAYS_SHOW_NAMETAG => new ByteMetadataProperty(1),
]; ];
$p[] = AddPlayerPacket::create( $p[] = AddActorPacket::create(
$uuid,
$name,
$this->entityId, //TODO: actor unique ID $this->entityId, //TODO: actor unique ID
"", $this->entityId,
$pos, //TODO: check offset EntityIds::FALLING_BLOCK,
$pos, //TODO: check offset (0.49?)
null, null,
0, 0,
0, 0,
0, 0,
ItemStackWrapper::legacy(ItemStack::null()), 0,
GameMode::SURVIVAL,
$actorMetadata,
UpdateAbilitiesPacket::create(0, 0, $this->entityId, []),
[], [],
"", $actorMetadata,
DeviceOS::UNKNOWN []
); );
$p[] = PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($uuid)]);
} }
return $p; return $p;