mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Merge branch 'next-minor' into next-major
This commit is contained in:
commit
c8a8e33fc1
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
run: echo ::set-output name=NAME::$(echo "${GITHUB_REPOSITORY,,}")
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.1.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.1.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.1.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
uses: docker/build-push-action@v3.1.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.20.0
|
||||
uses: shivammathur/setup-php@2.21.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
|
||||
|
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -198,7 +198,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.20.0
|
||||
uses: shivammathur/setup-php@2.21.0
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: php-cs-fixer:3.2
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 11103498ca761be83598f9759cc7196c167fcb7e
|
||||
Subproject commit f292501a703352ab793b07b7861f3e1b3860ed86
|
@ -12,3 +12,20 @@ Released 13th July 2022.
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.19.10.
|
||||
- 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.
|
||||
|
@ -50,6 +50,7 @@ use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
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\entity\EntityIds;
|
||||
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\inventory\ItemStackWrapper;
|
||||
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\player\Player;
|
||||
use pocketmine\utils\Limits;
|
||||
use pocketmine\world\sound\TotemUseSound;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use function array_fill;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_merge;
|
||||
@ -464,7 +468,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
|
||||
GameMode::SURVIVAL,
|
||||
$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
|
||||
"", //device ID (we intentionally don't send this - secvuln)
|
||||
DeviceOS::UNKNOWN //we intentionally don't send this (secvuln)
|
||||
|
@ -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))){
|
||||
|
@ -142,6 +142,11 @@ class InGamePacketHandler extends PacketHandler{
|
||||
protected float $lastRightClickTime = 0.0;
|
||||
protected ?UseItemTransactionData $lastRightClickData = null;
|
||||
|
||||
protected ?Vector3 $lastPlayerAuthInputPosition = null;
|
||||
protected ?float $lastPlayerAuthInputYaw = null;
|
||||
protected ?float $lastPlayerAuthInputPitch = null;
|
||||
protected ?int $lastPlayerAuthInputFlags = null;
|
||||
|
||||
public bool $forceMoveSync = false;
|
||||
|
||||
public function __construct(
|
||||
@ -164,9 +169,10 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function resolveOnOffInputFlags(PlayerAuthInputPacket $packet, int $startFlag, int $stopFlag) : ?bool{
|
||||
$enabled = $packet->hasFlag($startFlag);
|
||||
if($enabled !== $packet->hasFlag($stopFlag)){
|
||||
private function resolveOnOffInputFlags(int $inputFlags, int $startFlag, int $stopFlag) : ?bool{
|
||||
$enabled = ($inputFlags & (1 << $startFlag)) !== 0;
|
||||
$disabled = ($inputFlags & (1 << $stopFlag)) !== 0;
|
||||
if($enabled !== $disabled){
|
||||
return $enabled;
|
||||
}
|
||||
//neither flag was set, or both were set
|
||||
@ -175,51 +181,68 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
|
||||
$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)){
|
||||
$this->session->getLogger()->debug("Invalid movement received, contains NAN/INF components");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$yaw = fmod($packet->getYaw(), 360);
|
||||
$pitch = fmod($packet->getPitch(), 360);
|
||||
if($yaw < 0){
|
||||
$yaw += 360;
|
||||
if($rawYaw !== $this->lastPlayerAuthInputYaw || $rawPitch !== $this->lastPlayerAuthInputPitch){
|
||||
$this->lastPlayerAuthInputYaw = $rawYaw;
|
||||
$this->lastPlayerAuthInputPitch = $rawPitch;
|
||||
|
||||
$yaw = fmod($rawYaw, 360);
|
||||
$pitch = fmod($rawPitch, 360);
|
||||
if($yaw < 0){
|
||||
$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);
|
||||
|
||||
if($this->forceMoveSync && $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);
|
||||
//Still getting movements from before teleport, ignore them
|
||||
return false;
|
||||
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);
|
||||
//Still getting movements from before teleport, ignore them
|
||||
return false;
|
||||
}
|
||||
|
||||
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
|
||||
$this->forceMoveSync = false;
|
||||
}
|
||||
|
||||
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
|
||||
$this->forceMoveSync = false;
|
||||
$inputFlags = $packet->getInputFlags();
|
||||
if($inputFlags !== $this->lastPlayerAuthInputFlags){
|
||||
$this->lastPlayerAuthInputFlags = $inputFlags;
|
||||
|
||||
$sneaking = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING);
|
||||
$sprinting = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
|
||||
$swimming = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
|
||||
$gliding = $this->resolveOnOffInputFlags($packet, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
|
||||
$mismatch =
|
||||
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
|
||||
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
|
||||
($swimming !== null && !$this->player->toggleSwim($swimming)) |
|
||||
($gliding !== null && !$this->player->toggleGlide($gliding));
|
||||
if((bool) $mismatch){
|
||||
$this->player->sendData([$this->player]);
|
||||
$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 =
|
||||
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
|
||||
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
|
||||
($swimming !== null && !$this->player->toggleSwim($swimming)) |
|
||||
($gliding !== null && !$this->player->toggleGlide($gliding));
|
||||
if((bool) $mismatch){
|
||||
$this->player->sendData([$this->player]);
|
||||
}
|
||||
|
||||
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
|
||||
$this->player->jump();
|
||||
}
|
||||
}
|
||||
|
||||
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
|
||||
$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
|
||||
$this->player->handleMovement($newPos);
|
||||
}
|
||||
|
@ -1183,7 +1183,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$oldPos = $this->getLocation();
|
||||
$oldPos = $this->location;
|
||||
$distanceSquared = $newPos->distanceSquared($oldPos);
|
||||
|
||||
$revert = false;
|
||||
@ -1201,7 +1201,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* asking for help if you suffer the consequences of messing with this.
|
||||
*/
|
||||
$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;
|
||||
}elseif(!$this->getWorld()->isInLoadedTerrain($newPos)){
|
||||
$revert = true;
|
||||
@ -1209,9 +1209,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
if(!$revert && $distanceSquared != 0){
|
||||
$dx = $newPos->x - $this->location->x;
|
||||
$dy = $newPos->y - $this->location->y;
|
||||
$dz = $newPos->z - $this->location->z;
|
||||
$dx = $newPos->x - $oldPos->x;
|
||||
$dy = $newPos->y - $oldPos->y;
|
||||
$dz = $newPos->z - $oldPos->z;
|
||||
|
||||
$this->move($dx, $dy, $dz);
|
||||
}
|
||||
|
@ -33,19 +33,31 @@ use function spl_object_id;
|
||||
/**
|
||||
* 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
|
||||
* with no AsyncTask running. Therefore, an AsyncTask SHOULD NOT execute for more than a few seconds. For tasks that
|
||||
* run for a long time or infinitely, start another thread instead.
|
||||
* An AsyncTask is run by a thread pool of reusable threads, and doesn't have its own dedicated thread. A thread is
|
||||
* usually chosen from the pool at random to run the task (though a specific thread in the pool may be selected
|
||||
* 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.
|
||||
* 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
|
||||
* {@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.
|
||||
*
|
||||
* WARNING: Do not call PocketMine-MP API methods from other Threads!!
|
||||
*/
|
||||
abstract class AsyncTask extends \Threaded{
|
||||
/**
|
||||
|
@ -82,6 +82,12 @@ abstract class Timings{
|
||||
public static array $tileEntityTypeTimingMap = [];
|
||||
/** @var TimingsHandler[] */
|
||||
public static array $packetReceiveTimingMap = [];
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $packetDecodeTimingMap = [];
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $packetHandleTimingMap = [];
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
public static array $packetSendTimingMap = [];
|
||||
/** @var TimingsHandler[] */
|
||||
@ -195,6 +201,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{
|
||||
self::init();
|
||||
$pid = $pk->pid();
|
||||
|
@ -23,25 +23,20 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world\particle;
|
||||
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\convert\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\AddActorPacket;
|
||||
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\EntityMetadataProperties;
|
||||
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\GameMode;
|
||||
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;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
|
||||
|
||||
class FloatingTextParticle implements Particle{
|
||||
//TODO: HACK!
|
||||
@ -88,38 +83,34 @@ class FloatingTextParticle implements Particle{
|
||||
}
|
||||
|
||||
if(!$this->invisible){
|
||||
$uuid = Uuid::uuid4();
|
||||
$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 = (
|
||||
1 << EntityMetadataFlags::IMMOBILE
|
||||
);
|
||||
$actorMetadata = [
|
||||
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(
|
||||
$uuid,
|
||||
$name,
|
||||
$p[] = AddActorPacket::create(
|
||||
$this->entityId, //TODO: actor unique ID
|
||||
"",
|
||||
$pos, //TODO: check offset
|
||||
$this->entityId,
|
||||
EntityIds::FALLING_BLOCK,
|
||||
$pos, //TODO: check offset (0.49?)
|
||||
null,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
ItemStackWrapper::legacy(ItemStack::null()),
|
||||
GameMode::SURVIVAL,
|
||||
$actorMetadata,
|
||||
UpdateAbilitiesPacket::create(0, 0, $this->entityId, []),
|
||||
0,
|
||||
[],
|
||||
"",
|
||||
DeviceOS::UNKNOWN
|
||||
$actorMetadata,
|
||||
[]
|
||||
);
|
||||
|
||||
$p[] = PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($uuid)]);
|
||||
}
|
||||
|
||||
return $p;
|
||||
|
Loading…
x
Reference in New Issue
Block a user