Merge branch 'stable' into next-minor

# Conflicts:
#	src/pocketmine/Player.php
#	src/pocketmine/block/SnowLayer.php
This commit is contained in:
Dylan K. Taylor 2020-06-01 13:42:59 +01:00
commit f24be2b055
15 changed files with 93 additions and 38 deletions

View File

@ -25,3 +25,23 @@ Plugin developers should **only** update their required API to this version if y
- Fixed server crash when `network.compression-level` is overridden by a CLI parameter.
- Fixed moving entities spawning themselves to players registered on chunks when the players haven't received the chunk yet.
- Cocoa pods now drop cocoa beans when broken instead of the block itself.
# 3.12.3
- Core code is now analyzed using PHPStan level 8 (using baselines). While not all the code is level 8 compliant, this does mean that new code will be held to a higher standard, ensuring quality going forwards.
- Players no longer burn when melee-attacked by other players. (vanilla parity)
- Arrows shot by burning players are no longer on fire. (vanilla parity)
- Fixed a crash that could occur with plugins on Unix filesystems that had backslashes in their names.
- Cleaned up a whole bunch of unknowns in the protocol layer. Many new constants have been added.
- Fixed player walking sounds.
- Default generation queue size has been raised to 32 (previously 8). The previous default was selected in a time when PHP was much less performant than it is today, and in today's world it just needlessly slows things down.
- Double plants are now burned away by fire.
- Snow layers can now be stacked. (vanilla parity)
- Resource pack sending chunk size has been reduced to 128 KB (previously 1 MB). This change was made after analyzing the effects that larger pack chunk sizes have on RakNet. Given the technical evidence, a smaller size, while slightly less bandwidth-efficient, should be more manageable for RakNet due to lower split reassembly overhead and reduced memory pressure.
- Fixed "switching" (an exploit often complained about by PvP players). Now, the previous damage is subtracted from current damage when an entity is attacked while on cooldown. This means that attacking with a wooden sword and then diamond sword while attack cooldown is active will only deal as much damage as the diamond sword would have, instead of the combined total. This can be controlled using the `EntityDamageEvent::MODIFIER_PREVIOUS_DAMAGE_COOLDOWN` modifier. (vanilla parity)
- Fixed projectiles knocking mobs back in unexpected directions on collision.
- Fixed inventories not being synchronized on failed inventory transactions.
- Vector3s decoded from packets are no longer rounded directly. Instead, the player movement handler takes responsibility for rounding the coordinates to prevent anti cheat doing something it's not supposed to.
- `mobflame` particle can now be spawned using the `/particle` command.
- Fixed several internal errors that could occur while modifying writable books.
- Fixed swapping writable book pages not working in some cases.
- `WritableBook->getPageText()` no longer throws an exception when the page doesn't exist, but returns null (as it was originally intended to).

View File

@ -220,6 +220,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
private const MOVES_PER_TICK = 2;
private const MOVE_BACKLOG_SIZE = 100 * self::MOVES_PER_TICK; //100 ticks backlog (5 seconds)
private const RESOURCE_PACK_CHUNK_SIZE = 128 * 1024; //128KB
/**
* Validates the given username.
*/
@ -2112,7 +2114,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new ResourcePackDataInfoPacket();
$pk->packId = $pack->getPackId();
$pk->maxChunkSize = 1048576; //1MB
$pk->maxChunkSize = self::RESOURCE_PACK_CHUNK_SIZE;
$pk->chunkCount = (int) ceil($pack->getPackSize() / $pk->maxChunkSize);
$pk->compressedPackSize = $pack->getPackSize();
$pk->sha256 = $pack->getSha256();
@ -2278,7 +2280,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handleMovePlayer(MovePlayerPacket $packet) : bool{
$newPos = $packet->position->subtract(0, $this->baseOffset, 0);
$newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0);
if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3());
@ -2344,11 +2346,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
if($this->isSpectator()){
$this->sendAllInventories();
return true;
}
/** @var InventoryAction[] $actions */
$actions = [];
foreach($packet->actions as $networkInventoryAction){
@ -2442,7 +2439,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->setUsingItem(false);
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
}elseif($this->isCreative()){
$item = $this->inventory->getItemInHand();
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
@ -2548,7 +2545,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
if($this->hasItemCooldown($item)){
if($this->hasItemCooldown($item) or $this->isSpectator()){
$ev->setCancelled();
}
@ -2599,7 +2596,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$heldItem = $this->inventory->getItemInHand();
$oldItem = clone $heldItem;
if(!$this->canInteract($target, 8)){
if(!$this->canInteract($target, 8) or $this->isSpectator()){
$cancelled = true;
}elseif($target instanceof Player){
if(!$this->server->getConfigBool("pvp")){
@ -3097,8 +3094,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk = new ResourcePackChunkDataPacket();
$pk->packId = $pack->getPackId();
$pk->chunkIndex = $packet->chunkIndex;
$pk->data = $pack->getPackChunk(1048576 * $packet->chunkIndex, 1048576);
$pk->progress = (1048576 * $packet->chunkIndex);
$pk->data = $pack->getPackChunk(self::RESOURCE_PACK_CHUNK_SIZE * $packet->chunkIndex, self::RESOURCE_PACK_CHUNK_SIZE);
$pk->progress = (self::RESOURCE_PACK_CHUNK_SIZE * $packet->chunkIndex);
$this->dataPacket($pk);
return true;
}
@ -3119,14 +3116,26 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_ADD_PAGE:
if(!$newBook->pageExists($packet->pageNumber)){
//this may only come before a page which already exists
//TODO: the client can send insert-before actions on trailing client-side pages which cause odd behaviour on the server
return false;
}
$newBook->insertPage($packet->pageNumber, $packet->text);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_DELETE_PAGE:
if(!$newBook->pageExists($packet->pageNumber)){
return false;
}
$newBook->deletePage($packet->pageNumber);
$modifiedPages[] = $packet->pageNumber;
break;
case BookEditPacket::TYPE_SWAP_PAGES:
if(!$newBook->pageExists($packet->pageNumber) or !$newBook->pageExists($packet->secondaryPageNumber)){
//the client will create pages on its own without telling us until it tries to switch them
$newBook->addPage(max($packet->pageNumber, $packet->secondaryPageNumber));
}
$newBook->swapPages($packet->pageNumber, $packet->secondaryPageNumber);
$modifiedPages = [$packet->pageNumber, $packet->secondaryPageNumber];
break;

View File

@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.12.3";
const BASE_VERSION = "3.12.4";
const IS_DEVELOPMENT_BUILD = true;
const BUILD_NUMBER = 0;

View File

@ -124,4 +124,12 @@ class DoublePlant extends Flowable{
return parent::getAffectedBlocks();
}
public function getFlameEncouragement() : int{
return 60;
}
public function getFlammability() : int{
return 100;
}
}

View File

@ -42,7 +42,7 @@ class SnowLayer extends Flowable{
}
public function canBeReplaced() : bool{
return true;
return $this->meta < 7; //8 snow layers
}
public function getHardness() : float{
@ -57,9 +57,15 @@ class SnowLayer extends Flowable{
return TieredTool::TIER_WOODEN;
}
private function canBeSupportedBy(Block $b) : bool{
return $b->isSolid() or ($b->getId() === $this->getId() and $b->getDamage() === 7);
}
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
if($blockReplace->getSide(Vector3::SIDE_DOWN)->isSolid()){
//TODO: fix placement
if($blockReplace->getId() === $this->getId() and $blockReplace->getDamage() < 7){
$this->setDamage($blockReplace->getDamage() + 1);
}
if($this->canBeSupportedBy($blockReplace->getSide(Vector3::SIDE_DOWN))){
$this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true;
@ -69,7 +75,7 @@ class SnowLayer extends Flowable{
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Vector3::SIDE_DOWN)->isSolid()){
if(!$this->canBeSupportedBy($this->getSide(Vector3::SIDE_DOWN))){
$this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false, false);
}
}

View File

@ -37,6 +37,7 @@ use pocketmine\level\particle\CriticalParticle;
use pocketmine\level\particle\DustParticle;
use pocketmine\level\particle\EnchantmentTableParticle;
use pocketmine\level\particle\EnchantParticle;
use pocketmine\level\particle\EntityFlameParticle;
use pocketmine\level\particle\ExplodeParticle;
use pocketmine\level\particle\FlameParticle;
use pocketmine\level\particle\HappyVillagerParticle;
@ -204,7 +205,8 @@ class ParticleCommand extends VanillaCommand{
return new AngryVillagerParticle($pos);
case "forcefield":
return new BlockForceFieldParticle($pos, $data ?? 0);
case "mobflame":
return new EntityFlameParticle($pos);
}
if(strpos($name, "iconcrack_") === 0){

View File

@ -436,6 +436,9 @@ abstract class Living extends Entity implements Damageable{
* to effects or armour.
*/
public function applyDamageModifiers(EntityDamageEvent $source) : void{
if($this->lastDamageCause !== null and $this->attackTime > 0){
$source->setModifier(-$this->lastDamageCause->getBaseDamage(), EntityDamageEvent::MODIFIER_PREVIOUS_DAMAGE_COOLDOWN);
}
if($source->canBeReducedByArmor()){
//MCPE uses the same system as PC did pre-1.9
$source->setModifier(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
@ -514,11 +517,6 @@ abstract class Living extends Entity implements Damageable{
public function attack(EntityDamageEvent $source) : void{
if($this->noDamageTicks > 0){
$source->setCancelled();
}elseif($this->attackTime > 0){
$lastCause = $this->getLastDamageCause();
if($lastCause !== null and $lastCause->getBaseDamage() >= $source->getBaseDamage()){
$source->setCancelled();
}
}
if($this->hasEffect(Effect::FIRE_RESISTANCE) and (
@ -550,12 +548,14 @@ abstract class Living extends Entity implements Damageable{
$this->attackTime = $source->getAttackCooldown();
if($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($source instanceof EntityDamageByChildEntityEvent){
$e = $source->getChild();
if($source instanceof EntityDamageByChildEntityEvent){
$e = $source->getChild();
if($e !== null){
$motion = $e->getMotion();
$this->knockBack($e, $source->getBaseDamage(), $motion->x, $motion->z, $source->getKnockBack());
}
}elseif($source instanceof EntityDamageByEntityEvent){
$e = $source->getDamager();
if($e !== null){
$deltaX = $this->x - $e->x;
$deltaZ = $this->z - $e->z;

View File

@ -111,7 +111,7 @@ class FallingBlock extends Entity{
$this->flagForDespawn();
$block = $this->level->getBlock($pos);
if(($block->isTransparent() and !$block->canBeReplaced()) or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){
if(!$block->canBeReplaced() or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){
//FIXME: anvils are supposed to destroy torches
$this->getLevelNonNull()->dropItem($this, ItemFactory::get($this->getBlock(), $this->getDamage()));
}else{

View File

@ -40,6 +40,7 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
public const MODIFIER_CRITICAL = 7;
public const MODIFIER_TOTEM = 8;
public const MODIFIER_WEAPON_ENCHANTMENTS = 9;
public const MODIFIER_PREVIOUS_DAMAGE_COOLDOWN = 10;
public const CAUSE_CONTACT = 0;
public const CAUSE_ENTITY_ATTACK = 1;
@ -181,4 +182,8 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
public function setAttackCooldown(int $attackCooldown) : void{
$this->attackCooldown = $attackCooldown;
}
public function isCancelled() : bool{
return parent::isCancelled() or $this->getFinalDamage() <= 0;
}
}

View File

@ -293,7 +293,12 @@ class InventoryTransaction{
$this->shuffleActions();
$this->validate();
try{
$this->validate();
}catch(TransactionValidationException $e){
$this->sendInventories();
throw $e;
}
if(!$this->callExecuteEvent()){
$this->sendInventories();

View File

@ -83,7 +83,7 @@ class Bow extends Tool{
}
$ev = new EntityShootBowEvent($player, $this, $entity, $baseForce * 3);
if($baseForce < 0.1 or $diff < 5){
if($baseForce < 0.1 or $diff < 5 or $player->isSpectator()){
$ev->setCancelled();
}

View File

@ -50,7 +50,7 @@ class WritableBook extends Item{
*/
public function getPageText(int $pageId) : ?string{
$pages = $this->getNamedTag()->getListTag(self::TAG_PAGES);
if($pages === null){
if($pages === null or !$pages->isset($pageId)){
return null;
}

View File

@ -1858,7 +1858,7 @@ class Level implements ChunkManager, Metadatable{
if($player !== null){
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
if($this->checkSpawnProtection($player, $blockClicked)){
if($this->checkSpawnProtection($player, $blockClicked) or $player->isSpectator()){
$ev->setCancelled(); //set it to cancelled so plugins can bypass this
}
@ -1902,7 +1902,7 @@ class Level implements ChunkManager, Metadatable{
if($player !== null){
$ev = new BlockPlaceEvent($player, $hand, $blockReplace, $blockClicked, $item);
if($this->checkSpawnProtection($player, $blockReplace)){
if($this->checkSpawnProtection($player, $blockReplace) or $player->isSpectator()){
$ev->setCancelled();
}

View File

@ -547,9 +547,9 @@ class NetworkBinaryStream extends BinaryStream{
*/
public function getVector3() : Vector3{
return new Vector3(
$this->getRoundedLFloat(4),
$this->getRoundedLFloat(4),
$this->getRoundedLFloat(4)
$this->getLFloat(),
$this->getLFloat(),
$this->getLFloat()
);
}

View File

@ -128,7 +128,7 @@ chunk-ticking:
chunk-generation:
#Max. amount of chunks in the waiting queue to be populated
population-queue-size: 8
population-queue-size: 32
ticks-per:
autosave: 6000