Merge branch 'stable' into next-minor

This commit is contained in:
Dylan K. Taylor 2022-09-24 18:07:56 +01:00
commit a7dfa0907c
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
11 changed files with 168 additions and 30 deletions

@ -1 +1 @@
Subproject commit cf79c0172283a0a26f2c3695af260d100e0fdabd Subproject commit 50062b5861235fbb9fe6e5d5b30684f4cb464470

View File

@ -53,7 +53,7 @@
"webmozart/path-util": "^2.3" "webmozart/path-util": "^2.3"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.8.5", "phpstan/phpstan": "1.8.6",
"phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0", "phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2" "phpunit/phpunit": "^9.2"

28
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0f6c845836d4ec6f221415d2f9dd1fc5", "content-hash": "29badb76ebb63167718947cad48bd37d",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -1506,16 +1506,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.8.5", "version": "1.8.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20" "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6598a5ff12ca4499a836815e08b4d77a2ddeb20", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c386ab2741e64cc9e21729f891b28b2b10fe6618",
"reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20", "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1545,7 +1545,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.5" "source": "https://github.com/phpstan/phpstan/tree/1.8.6"
}, },
"funding": [ "funding": [
{ {
@ -1561,7 +1561,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-09-07T16:05:32+00:00" "time": "2022-09-23T09:54:39+00:00"
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
@ -1617,21 +1617,21 @@
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "1.4.3", "version": "1.4.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "431b3d6e8040075de196680cd5bc95735987b4ae" "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/431b3d6e8040075de196680cd5bc95735987b4ae", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
"reference": "431b3d6e8040075de196680cd5bc95735987b4ae", "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.3" "phpstan/phpstan": "^1.8.6"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0", "nikic/php-parser": "^4.13.0",
@ -1659,9 +1659,9 @@
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.3" "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4"
}, },
"time": "2022-08-26T15:05:46+00:00" "time": "2022-09-21T11:38:17+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -78,25 +78,47 @@ class Block{
$this->position = clone $this->position; $this->position = clone $this->position;
} }
/**
* Returns an object containing information about how to identify and store this block type, such as its legacy
* numeric ID(s), tile type (if any), and legacy variant metadata.
*/
public function getIdInfo() : BlockIdentifier{ public function getIdInfo() : BlockIdentifier{
return $this->idInfo; return $this->idInfo;
} }
/**
* Returns the printable English name of the block.
*/
public function getName() : string{ public function getName() : string{
return $this->fallbackName; return $this->fallbackName;
} }
/**
* @deprecated
*
* Returns the legacy numeric Minecraft block ID.
*/
public function getId() : int{ public function getId() : int{
return $this->idInfo->getBlockId(); return $this->idInfo->getBlockId();
} }
/** /**
* @internal * @internal
*
* Returns the full blockstate ID of this block. This is a compact way of representing a blockstate used to store
* blocks in chunks at runtime.
*
* This ID can be used to later obtain a copy of this block using {@link BlockFactory::get()}.
*/ */
public function getFullId() : int{ public function getFullId() : int{
return ($this->getId() << self::INTERNAL_METADATA_BITS) | $this->getMeta(); return ($this->getId() << self::INTERNAL_METADATA_BITS) | $this->getMeta();
} }
/**
* Returns the block as an item.
* State information such as facing, powered/unpowered, open/closed, etc., is discarded.
* Type information such as colour, wood type, etc. is preserved.
*/
public function asItem() : Item{ public function asItem() : Item{
return ItemFactory::getInstance()->get( return ItemFactory::getInstance()->get(
$this->idInfo->getItemId(), $this->idInfo->getItemId(),
@ -104,6 +126,12 @@ class Block{
); );
} }
/**
* @deprecated
*
* Returns the legacy Minecraft block meta value. This is a mixed-purpose value, which is used to store different
* things for different blocks.
*/
public function getMeta() : int{ public function getMeta() : int{
$stateMeta = $this->writeStateToMeta(); $stateMeta = $this->writeStateToMeta();
assert(($stateMeta & ~$this->getStateBitmask()) === 0); assert(($stateMeta & ~$this->getStateBitmask()) === 0);
@ -116,6 +144,7 @@ class Block{
/** /**
* Returns a bitmask used to extract state bits from block metadata. * Returns a bitmask used to extract state bits from block metadata.
* This is used to remove unwanted information from the legacy meta value when getting the block as an item.
*/ */
public function getStateBitmask() : int{ public function getStateBitmask() : int{
return 0; return 0;
@ -143,6 +172,12 @@ class Block{
$this->collisionBoxes = null; $this->collisionBoxes = null;
} }
/**
* Writes information about the block into the world. This writes the blockstate ID into the chunk, and creates
* and/or removes tiles as necessary.
*
* Note: Do not call this directly. Pass the block to {@link World::setBlock()} instead.
*/
public function writeStateToWorld() : void{ public function writeStateToWorld() : void{
$world = $this->position->getWorld(); $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()); $world->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getFullId());
@ -168,7 +203,7 @@ class Block{
} }
/** /**
* Returns a type ID that identifies this type of block. This does not include information like facing, colour, * Returns a type ID that identifies this type of block. This does not include information like facing, open/closed,
* powered/unpowered, etc. * powered/unpowered, etc.
*/ */
public function getTypeId() : int{ public function getTypeId() : int{
@ -199,22 +234,36 @@ class Block{
return true; return true;
} }
/**
* Returns whether this block can be replaced by another block placed in the same position.
*/
public function canBeReplaced() : bool{ public function canBeReplaced() : bool{
return false; return false;
} }
/**
* Returns whether this block can replace the given block in the given placement conditions.
* This is used to allow slabs of the same type to combine into double slabs.
*/
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return $blockReplace->canBeReplaced(); return $blockReplace->canBeReplaced();
} }
/** /**
* Places the Block, using block space and block target, and side. Returns if the block has been placed. * Generates a block transaction to set all blocks affected by placing this block. Usually this is just the block
* itself, but may be multiple blocks in some cases (such as doors).
*
* @return bool whether the placement should go ahead
*/ */
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$tx->addBlock($blockReplace->position, $this); $tx->addBlock($blockReplace->position, $this);
return true; return true;
} }
/**
* Called immediately after the block has been placed in the world. Since placement uses a block transaction, some
* things may not be possible until after the transaction has been executed.
*/
public function onPostPlace() : void{ public function onPostPlace() : void{
} }
@ -254,7 +303,7 @@ class Block{
/** /**
* Called when this block is randomly updated due to chunk ticking. * Called when this block is randomly updated due to chunk ticking.
* WARNING: This will not be called if ticksRandomly() does not return true! * WARNING: This will not be called if {@link Block::ticksRandomly()} does not return true!
*/ */
public function onRandomTick() : void{ public function onRandomTick() : void{
@ -275,8 +324,7 @@ class Block{
} }
/** /**
* Called when this block is attacked (left-clicked). This is called when a player left-clicks the block to try and * Called when this block is attacked (left-clicked) by a player attempting to start breaking it in survival.
* start to break it in survival mode.
* *
* @return bool if an action took place, prevents starting to break the block if true. * @return bool if an action took place, prevents starting to break the block if true.
*/ */
@ -284,11 +332,19 @@ class Block{
return false; return false;
} }
/**
* Returns a multiplier applied to the velocity of entities moving on top of this block. A higher value will make
* the block more slippery (like ice).
*
* @return float 0.0-1.0
*/
public function getFrictionFactor() : float{ public function getFrictionFactor() : float{
return 0.6; return 0.6;
} }
/** /**
* Returns the amount of light emitted by this block.
*
* @return int 0-15 * @return int 0-15
*/ */
public function getLightLevel() : int{ public function getLightLevel() : int{
@ -331,10 +387,6 @@ class Block{
return false; return false;
} }
public function hasEntityCollision() : bool{
return false;
}
/** /**
* Returns whether entities can climb up this block. * Returns whether entities can climb up this block.
*/ */
@ -342,10 +394,6 @@ class Block{
return false; return false;
} }
public function addVelocityToEntity(Entity $entity) : ?Vector3{
return null;
}
final public function getPosition() : Position{ final public function getPosition() : Position{
return $this->position; return $this->position;
} }
@ -428,6 +476,7 @@ class Block{
/** /**
* Returns the item that players will equip when middle-clicking on this block. * Returns the item that players will equip when middle-clicking on this block.
* If addUserData is true, additional data may be added, such as banner patterns, chest contents, etc.
*/ */
public function getPickedItem(bool $addUserData = false) : Item{ public function getPickedItem(bool $addUserData = false) : Item{
$item = $this->asItem(); $item = $this->asItem();
@ -551,7 +600,7 @@ class Block{
} }
/** /**
* Checks for collision against an AxisAlignedBB * Returns whether any of the block's collision boxes intersect with the given AxisAlignedBB.
*/ */
public function collidesWithBB(AxisAlignedBB $bb) : bool{ public function collidesWithBB(AxisAlignedBB $bb) : bool{
foreach($this->getCollisionBoxes() as $bb2){ foreach($this->getCollisionBoxes() as $bb2){
@ -563,10 +612,21 @@ class Block{
return false; return false;
} }
/**
* Returns whether the block has actions to be executed when an entity enters its cell (full cube space).
*
* @see Block::onEntityInside()
*/
public function hasEntityCollision() : bool{
return false;
}
/** /**
* Called when an entity's bounding box clips inside this block's cell. Note that the entity may not be intersecting * Called when an entity's bounding box clips inside this block's cell. Note that the entity may not be intersecting
* with the collision box or bounding box. * with the collision box or bounding box.
* *
* WARNING: This will not be called if {@link Block::hasEntityCollision()} returns false.
*
* @return bool Whether the block is still the same after the intersection. If it changed (e.g. due to an explosive * @return bool Whether the block is still the same after the intersection. If it changed (e.g. due to an explosive
* being ignited), this should return false. * being ignited), this should return false.
*/ */
@ -574,6 +634,19 @@ class Block{
return true; return true;
} }
/**
* Returns a direction vector describing which way an entity intersecting this block should be pushed.
* This is used by liquids to push entities in liquid currents.
*
* The returned vector is summed with vectors from every other block the entity is intersecting, and normalized to
* produce a final direction vector.
*
* WARNING: This will not be called if {@link Block::hasEntityCollision()} does not return true!
*/
public function addVelocityToEntity(Entity $entity) : ?Vector3{
return null;
}
/** /**
* Called when an entity lands on this block (usually due to falling). * Called when an entity lands on this block (usually due to falling).
* @return float|null The new vertical velocity of the entity, or null if unchanged. * @return float|null The new vertical velocity of the entity, or null if unchanged.
@ -583,6 +656,13 @@ class Block{
} }
/** /**
* Returns an array of collision bounding boxes for this block.
* These are used for:
* - entity movement collision checks (to ensure entities can't clip through blocks)
* - projectile flight paths
* - block placement (to ensure the player can't place blocks inside itself or another entity)
* - anti-cheat checks in plugins
*
* @return AxisAlignedBB[] * @return AxisAlignedBB[]
*/ */
final public function getCollisionBoxes() : array{ final public function getCollisionBoxes() : array{
@ -613,6 +693,10 @@ class Block{
return [AxisAlignedBB::one()]; return [AxisAlignedBB::one()];
} }
/**
* Returns the type of support that the block can provide on the given face. This is used to determine whether
* blocks placed on the given face can be supported by this block.
*/
public function getSupportType(int $facing) : SupportType{ public function getSupportType(int $facing) : SupportType{
return SupportType::FULL(); return SupportType::FULL();
} }
@ -623,6 +707,10 @@ class Block{
return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube(); return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube();
} }
/**
* Performs a ray trace along the line between the two positions using the block's collision boxes.
* Returns the intersection point closest to pos1, or null if no intersection occurred.
*/
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{ public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
$bbs = $this->getCollisionBoxes(); $bbs = $this->getCollisionBoxes();
if(count($bbs) === 0){ if(count($bbs) === 0){

View File

@ -26,6 +26,10 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
/**
* "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any
* collision boxes, and can't provide support for other blocks.
*/
abstract class Flowable extends Transparent{ abstract class Flowable extends Transparent{
public function canBeFlowedInto() : bool{ public function canBeFlowedInto() : bool{

View File

@ -23,6 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
/**
* Opaque blocks do not allow light to pass through. They are usually collidable full-cube blocks.
* Most blocks in Minecraft fall into this category.
*/
class Opaque extends Block{ class Opaque extends Block{
public function isSolid() : bool{ public function isSolid() : bool{

View File

@ -29,6 +29,9 @@ use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use function count; use function count;
/**
* Thin blocks behave like glass panes. They connect to full-cube blocks horizontally adjacent to them if possible.
*/
class Thin extends Transparent{ class Thin extends Transparent{
/** @var bool[] facing => dummy */ /** @var bool[] facing => dummy */
protected array $connections = []; protected array $connections = [];

View File

@ -23,6 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
/**
* Transparent blocks do not block any light from propagating through them.
*
* Note: This does **not** imply that the block is **visually** transparent. For example, chests allow light to pass
* through, but the player cannot see through them except at the edges.
*/
class Transparent extends Block{ class Transparent extends Block{
public function isTransparent() : bool{ public function isTransparent() : bool{

View File

@ -25,6 +25,9 @@ namespace pocketmine\block;
use pocketmine\item\Item; use pocketmine\item\Item;
/**
* Represents a block which is unrecognized or not implemented.
*/
class UnknownBlock extends Transparent{ class UnknownBlock extends Transparent{
public function __construct(BlockIdentifier $idInfo, BlockBreakInfo $breakInfo){ public function __construct(BlockIdentifier $idInfo, BlockBreakInfo $breakInfo){

View File

@ -33,5 +33,8 @@ class HealthBoostEffect extends Effect{
public function remove(Living $entity, EffectInstance $instance) : void{ public function remove(Living $entity, EffectInstance $instance) : void{
$entity->setMaxHealth($entity->getMaxHealth() - 4 * $instance->getEffectLevel()); $entity->setMaxHealth($entity->getMaxHealth() - 4 * $instance->getEffectLevel());
if($entity->getHealth() > $entity->getMaxHealth()){
$entity->setHealth($entity->getMaxHealth());
}
} }
} }

View File

@ -418,6 +418,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
return $this->lastPlayed - $this->firstPlayed > 1; // microtime(true) - microtime(true) may have less than one millisecond difference return $this->lastPlayed - $this->firstPlayed > 1; // microtime(true) - microtime(true) may have less than one millisecond difference
} }
/**
* Sets whether the player is allowed to toggle flight mode.
*
* If set to false, the player will be locked in its current flight mode (flying/not flying), and attempts by the
* player to enter or exit flight mode will be prevented.
*
* Note: Setting this to false DOES NOT change whether the player is currently flying. Use
* {@link Player::setFlying()} for that purpose.
*/
public function setAllowFlight(bool $value) : void{ public function setAllowFlight(bool $value) : void{
if($this->allowFlight !== $value){ if($this->allowFlight !== $value){
$this->allowFlight = $value; $this->allowFlight = $value;
@ -425,10 +434,24 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
} }
} }
/**
* Returns whether the player is allowed to toggle its flight state.
*
* If false, the player is locked in its current flight mode (flying/not flying), and attempts by the player to
* enter or exit flight mode will be prevented.
*/
public function getAllowFlight() : bool{ public function getAllowFlight() : bool{
return $this->allowFlight; return $this->allowFlight;
} }
/**
* Sets whether the player's movement may be obstructed by blocks with collision boxes.
* If set to false, the player can move through any block unobstructed.
*
* Note: Enabling flight mode in conjunction with this is recommended. A non-flying player will simply fall through
* the ground into the void.
* @see Player::setFlying()
*/
public function setHasBlockCollision(bool $value) : void{ public function setHasBlockCollision(bool $value) : void{
if($this->blockCollision !== $value){ if($this->blockCollision !== $value){
$this->blockCollision = $value; $this->blockCollision = $value;
@ -436,6 +459,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
} }
} }
/**
* Returns whether blocks may obstruct the player's movement.
* If false, the player can move through any block unobstructed.
*/
public function hasBlockCollision() : bool{ public function hasBlockCollision() : bool{
return $this->blockCollision; return $this->blockCollision;
} }