mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 17:29:44 +00:00
Merge 'minor-next' into 'major-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/11865975725
This commit is contained in:
commit
9e2d91bae6
4
.github/workflows/team-pr-auto-approve.yml
vendored
4
.github/workflows/team-pr-auto-approve.yml
vendored
@ -14,8 +14,8 @@ on:
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
approve:
|
||||
name: Auto approve
|
||||
dispatch:
|
||||
name: Request approval
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityMotionEvent;
|
||||
use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\entity\EntitySpawnEvent;
|
||||
use pocketmine\event\entity\EntityTeleportEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector2;
|
||||
@ -1560,6 +1561,13 @@ abstract class Entity{
|
||||
$this->hasSpawned = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item that players will equip when middle-clicking on this entity.
|
||||
*/
|
||||
public function getPickedItem() : ?Item{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags the entity to be removed from the world on the next tick.
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\entity;
|
||||
use pocketmine\entity\animation\SquidInkCloudAnimation;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -124,4 +125,8 @@ class Squid extends WaterAnimal{
|
||||
VanillaItems::INK_SAC()->setCount(mt_rand(1, 3))
|
||||
];
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::SQUID_SPAWN_EGG();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
@ -87,6 +89,10 @@ class Villager extends Living implements Ageable{
|
||||
return $this->baby;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::VILLAGER_SPAWN_EGG();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby);
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use function mt_rand;
|
||||
@ -65,4 +66,8 @@ class Zombie extends Living{
|
||||
//TODO: check for equipment and whether it's a baby
|
||||
return 5;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::ZOMBIE_SPAWN_EGG();
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\entity\Location;
|
||||
use pocketmine\event\entity\EntityBlockChangeEvent;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -194,6 +195,10 @@ class FallingBlock extends Entity{
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return $this->block->asItem();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -165,6 +166,10 @@ class Painting extends Entity{
|
||||
));
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaItems::PAINTING();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the painting motive (which image is displayed on the painting)
|
||||
*/
|
||||
|
@ -23,11 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\object;
|
||||
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntitySizeInfo;
|
||||
use pocketmine\entity\Explosive;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityPreExplodeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
@ -127,6 +129,10 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
}
|
||||
}
|
||||
|
||||
public function getPickedItem() : ?Item{
|
||||
return VanillaBlocks::TNT()->setWorksUnderwater($this->worksUnderwater)->asItem();
|
||||
}
|
||||
|
||||
protected function syncNetworkData(EntityMetadataCollection $properties) : void{
|
||||
parent::syncNetworkData($properties);
|
||||
|
||||
|
53
src/event/player/PlayerEntityPickEvent.php
Normal file
53
src/event/player/PlayerEntityPickEvent.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player middle-clicks on an entity to get an item in creative mode.
|
||||
*/
|
||||
class PlayerEntityPickEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
private Entity $entityClicked,
|
||||
private Item $resultItem
|
||||
){
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function getEntity() : Entity{
|
||||
return $this->entityClicked;
|
||||
}
|
||||
|
||||
public function getResultItem() : Item{
|
||||
return $this->resultItem;
|
||||
}
|
||||
}
|
@ -42,6 +42,9 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
|
||||
protected Vector3 $touchVector;
|
||||
|
||||
protected bool $useItem = true;
|
||||
protected bool $useBlock = true;
|
||||
|
||||
public function __construct(
|
||||
Player $player,
|
||||
protected Item $item,
|
||||
@ -73,4 +76,28 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
public function getFace() : int{
|
||||
return $this->blockFace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the item may react to the interaction. If disabled, items such as spawn eggs will not activate.
|
||||
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
|
||||
*/
|
||||
public function useItem() : bool{ return $this->useItem; }
|
||||
|
||||
/**
|
||||
* Sets whether the used item may react to the interaction. If false, items such as spawn eggs will not activate.
|
||||
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
|
||||
*/
|
||||
public function setUseItem(bool $useItem) : void{ $this->useItem = $useItem; }
|
||||
|
||||
/**
|
||||
* Returns whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
|
||||
* respond, containers will not open, etc.
|
||||
*/
|
||||
public function useBlock() : bool{ return $this->useBlock; }
|
||||
|
||||
/**
|
||||
* Sets whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
|
||||
* respond, containers will not open, etc.
|
||||
*/
|
||||
public function setUseBlock(bool $useBlock) : void{ $this->useBlock = $useBlock; }
|
||||
}
|
||||
|
@ -493,15 +493,18 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, $data->getFace());
|
||||
}
|
||||
$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos);
|
||||
//always sync this in case plugins caused a different result than the client expected
|
||||
//we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating
|
||||
//more information up the stack. For now I think this is good enough.
|
||||
//if only the client would tell us what blocks it thinks changed...
|
||||
$this->syncBlocksNearby($vBlockPos, $data->getFace());
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->breakBlock($vBlockPos)){
|
||||
$this->onFailedBlockAction($vBlockPos, null);
|
||||
$this->syncBlocksNearby($vBlockPos, null);
|
||||
}
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
@ -529,9 +532,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used to execute rollbacks when an action fails on a block.
|
||||
* Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction.
|
||||
*/
|
||||
private function onFailedBlockAction(Vector3 $blockPos, ?int $face) : void{
|
||||
private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{
|
||||
if($blockPos->distanceSquared($this->player->getLocation()) < 10000){
|
||||
$blocks = $blockPos->sidesArray();
|
||||
if($face !== null){
|
||||
@ -668,7 +671,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{
|
||||
return false; //TODO
|
||||
return $this->player->pickEntity($packet->actorUniqueId);
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
@ -682,7 +685,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
case PlayerAction::START_BREAK:
|
||||
self::validateFacing($face);
|
||||
if(!$this->player->attackBlock($pos, $face)){
|
||||
$this->onFailedBlockAction($pos, $face);
|
||||
$this->syncBlocksNearby($pos, $face);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -998,7 +1001,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ());
|
||||
if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){
|
||||
if(!$lectern->onPageTurn($packet->page)){
|
||||
$this->onFailedBlockAction($lectern->getPosition(), null);
|
||||
$this->syncBlocksNearby($lectern->getPosition(), null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ use pocketmine\event\player\PlayerDisplayNameChangeEvent;
|
||||
use pocketmine\event\player\PlayerDropItemEvent;
|
||||
use pocketmine\event\player\PlayerEmoteEvent;
|
||||
use pocketmine\event\player\PlayerEntityInteractEvent;
|
||||
use pocketmine\event\player\PlayerEntityPickEvent;
|
||||
use pocketmine\event\player\PlayerExhaustEvent;
|
||||
use pocketmine\event\player\PlayerGameModeChangeEvent;
|
||||
use pocketmine\event\player\PlayerInteractEvent;
|
||||
@ -1714,29 +1715,58 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
if($existingSlot !== -1){
|
||||
if($existingSlot < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setHeldItemIndex($existingSlot);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot);
|
||||
}
|
||||
}else{
|
||||
$firstEmpty = $this->inventory->firstEmpty();
|
||||
if($firstEmpty === -1){ //full inventory
|
||||
$this->inventory->setItemInHand($item);
|
||||
}elseif($firstEmpty < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setItem($firstEmpty, $item);
|
||||
$this->inventory->setHeldItemIndex($firstEmpty);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
$this->equipOrAddPickedItem($existingSlot, $item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pickEntity(int $entityId) : bool{
|
||||
$entity = $this->getWorld()->getEntity($entityId);
|
||||
if($entity === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$item = $entity->getPickedItem();
|
||||
if($item === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
$ev = new PlayerEntityPickEvent($this, $entity, $item);
|
||||
$existingSlot = $this->inventory->first($item);
|
||||
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$this->equipOrAddPickedItem($existingSlot, $item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{
|
||||
if($existingSlot !== -1){
|
||||
if($existingSlot < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setHeldItemIndex($existingSlot);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot);
|
||||
}
|
||||
}else{
|
||||
$firstEmpty = $this->inventory->firstEmpty();
|
||||
if($firstEmpty === -1){ //full inventory
|
||||
$this->inventory->setItemInHand($item);
|
||||
}elseif($firstEmpty < $this->inventory->getHotbarSize()){
|
||||
$this->inventory->setItem($firstEmpty, $item);
|
||||
$this->inventory->setHeldItemIndex($firstEmpty);
|
||||
}else{
|
||||
$this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a left-click (attack) action on the block.
|
||||
*
|
||||
|
@ -2173,19 +2173,25 @@ class World implements ChunkManager{
|
||||
|
||||
if($player !== null){
|
||||
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
if($player->isSneaking()){
|
||||
$ev->setUseItem(false);
|
||||
$ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand
|
||||
}
|
||||
if($player->isSpectator()){
|
||||
$ev->cancel(); //set it to cancelled so plugins can bypass this
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
|
||||
if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
|
||||
if($result !== ItemUseResult::NONE){
|
||||
return $result === ItemUseResult::SUCCESS;
|
||||
if($ev->useItem()){
|
||||
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
|
||||
if($result !== ItemUseResult::NONE){
|
||||
return $result === ItemUseResult::SUCCESS;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
|
27
start.sh
27
start.sh
@ -44,6 +44,27 @@ fi
|
||||
|
||||
LOOPS=0
|
||||
|
||||
handle_exit_code() {
|
||||
local exitcode=$1
|
||||
if [ "$exitcode" -eq 134 ] || [ "$exitcode" -eq 139 ]; then #SIGABRT/SIGSEGV
|
||||
echo ""
|
||||
echo "ERROR: The server process was killed due to a critical error (code $exitcode) which could indicate a problem with PHP."
|
||||
echo "Updating your PHP binary is recommended."
|
||||
echo "If this keeps happening, please open a bug report."
|
||||
echo ""
|
||||
elif [ "$exitcode" -eq 143 ]; then #SIGKILL, maybe user intervention
|
||||
echo ""
|
||||
echo "WARNING: Server was forcibly killed!"
|
||||
echo "If you didn't kill the server manually, this probably means the server used too much memory and was killed by the system's OOM Killer."
|
||||
echo "Please ensure your system has enough available RAM."
|
||||
echo ""
|
||||
elif [ "$exitcode" -ne 0 ] && [ "$exitcode" -ne 137 ]; then #normal exit / SIGTERM
|
||||
echo ""
|
||||
echo "WARNING: Server did not shut down correctly! (code $exitcode)"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
set +e
|
||||
|
||||
if [ "$DO_LOOP" == "yes" ]; then
|
||||
@ -52,11 +73,15 @@ if [ "$DO_LOOP" == "yes" ]; then
|
||||
echo "Restarted $LOOPS times"
|
||||
fi
|
||||
"$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
handle_exit_code $?
|
||||
echo "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart."
|
||||
echo ""
|
||||
sleep 5
|
||||
((LOOPS++))
|
||||
done
|
||||
else
|
||||
exec "$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
"$PHP_BINARY" "$POCKETMINE_FILE" "$@"
|
||||
exitcode=$?
|
||||
handle_exit_code $exitcode
|
||||
exit $exitcode
|
||||
fi
|
||||
|
Loading…
x
Reference in New Issue
Block a user