Merge branch 'master' into mcpe-1.2

This commit is contained in:
Dylan K. Taylor
2017-08-27 16:09:23 +01:00
205 changed files with 1131 additions and 1493 deletions

View File

@ -26,28 +26,8 @@ declare(strict_types=1);
*/
namespace pocketmine\level;
use pocketmine\block\Beetroot;
use pocketmine\block\Block;
use pocketmine\block\BlockFactory;
use pocketmine\block\BrownMushroom;
use pocketmine\block\Cactus;
use pocketmine\block\Carrot;
use pocketmine\block\Farmland;
use pocketmine\block\Fire;
use pocketmine\block\Grass;
use pocketmine\block\Ice;
use pocketmine\block\Leaves;
use pocketmine\block\Leaves2;
use pocketmine\block\MelonStem;
use pocketmine\block\Mycelium;
use pocketmine\block\NetherWartPlant;
use pocketmine\block\Potato;
use pocketmine\block\PumpkinStem;
use pocketmine\block\RedMushroom;
use pocketmine\block\Sapling;
use pocketmine\block\SnowLayer;
use pocketmine\block\Sugarcane;
use pocketmine\block\Wheat;
use pocketmine\entity\Arrow;
use pocketmine\entity\Effect;
use pocketmine\entity\Entity;
@ -227,30 +207,8 @@ class Level implements ChunkManager, Metadatable{
private $chunkTickList = [];
private $chunksPerTick;
private $clearChunksOnTick;
private $randomTickBlocks = [
Block::GRASS => Grass::class,
Block::SAPLING => Sapling::class,
Block::LEAVES => Leaves::class,
Block::WHEAT_BLOCK => Wheat::class,
Block::FARMLAND => Farmland::class,
Block::SNOW_LAYER => SnowLayer::class,
Block::ICE => Ice::class,
Block::CACTUS => Cactus::class,
Block::SUGARCANE_BLOCK => Sugarcane::class,
Block::RED_MUSHROOM => RedMushroom::class,
Block::BROWN_MUSHROOM => BrownMushroom::class,
Block::PUMPKIN_STEM => PumpkinStem::class,
Block::MELON_STEM => MelonStem::class,
//Block::VINE => true,
Block::MYCELIUM => Mycelium::class,
//Block::COCOA_BLOCK => true,
Block::CARROT_BLOCK => Carrot::class,
Block::POTATO_BLOCK => Potato::class,
Block::LEAVES2 => Leaves2::class,
Block::FIRE => Fire::class,
Block::BEETROOT_BLOCK => Beetroot::class,
Block::NETHER_WART_PLANT => NetherWartPlant::class
];
/** @var \SplFixedArray<Block> */
private $randomTickBlocks = null;
/** @var LevelTimings */
public $timings;
@ -344,10 +302,19 @@ class Level implements ChunkManager, Metadatable{
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", true);
$this->cacheChunks = (bool) $this->server->getProperty("chunk-sending.cache-chunks", false);
$this->randomTickBlocks = \SplFixedArray::fromArray(array_filter(BlockFactory::$list->toArray(), function(Block $block = null){
return $block !== null and $block->ticksRandomly();
}));
$this->randomTickBlocks->setSize(256);
$dontTickBlocks = $this->server->getProperty("chunk-ticking.disable-block-ticking", []);
foreach($dontTickBlocks as $id){
if(isset($this->randomTickBlocks[$id])){
unset($this->randomTickBlocks[$id]);
try{
if(isset($this->randomTickBlocks[$id])){
$this->randomTickBlocks[$id] = null;
}
}catch(\RuntimeException $e){
//index out of bounds
}
}
@ -912,11 +879,11 @@ class Level implements ChunkManager, Metadatable{
}
public function addRandomTickedBlock(int $id){
$this->randomTickBlocks[$id] = get_class(BlockFactory::$list[$id]);
$this->randomTickBlocks[$id] = BlockFactory::get($id);
}
public function removeRandomTickedBlock(int $id){
unset($this->randomTickBlocks[$id]);
$this->randomTickBlocks[$id] = null;
}
private function tickChunks(){
@ -964,17 +931,18 @@ class Level implements ChunkManager, Metadatable{
foreach($chunk->getSubChunks() as $Y => $subChunk){
if(!$subChunk->isEmpty()){
$k = mt_rand(0, 0x7fffffff);
for($i = 0; $i < 3; ++$i, $k >>= 10){
for($i = 0; $i < 3; ++$i){
$k = mt_rand(0, 0xfff);
$x = $k & 0x0f;
$y = ($k >> 8) & 0x0f;
$z = ($k >> 16) & 0x0f;
$y = ($k >> 4) & 0x0f;
$z = ($k >> 8) & 0x0f;
$blockId = $subChunk->getBlockId($x, $y, $z);
if(isset($this->randomTickBlocks[$blockId])){
$class = $this->randomTickBlocks[$blockId];
if($this->randomTickBlocks[$blockId] !== null){
/** @var Block $block */
$block = new $class($subChunk->getBlockData($x, $y, $z));
$block = clone $this->randomTickBlocks[$blockId];
$block->setDamage($subChunk->getBlockData($x, $y, $z));
$block->x = $chunkX * 16 + $x;
$block->y = ($Y << 4) + $y;
$block->z = $chunkZ * 16 + $z;
@ -1567,6 +1535,27 @@ class Level implements ChunkManager, Metadatable{
return null;
}
/**
* Checks if the level spawn protection radius will prevent the player from using items or building at the specified
* Vector3 position.
*
* @param Player $player
* @param Vector3 $vector
*
* @return bool false if spawn protection cancelled the action, true if not.
*/
protected function checkSpawnProtection(Player $player, Vector3 $vector) : bool{
if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
$t = new Vector2($vector->x, $vector->z);
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){
return true;
}
}
return false;
}
/**
* Tries to break a block using a item, including Player time checks if available
* It'll try to lower the durability if Item is a tool, and set it to Air if broken.
@ -1590,12 +1579,8 @@ class Level implements ChunkManager, Metadatable{
if(($player->isSurvival() and $item instanceof Item and !$target->isBreakable($item)) or $player->isSpectator()){
$ev->setCancelled();
}elseif(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
$t = new Vector2($target->x, $target->z);
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
$ev->setCancelled();
}
}elseif($this->checkSpawnProtection($player, $target)){
$ev->setCancelled(); //set it to cancelled so plugins can bypass this
}
if($player->isAdventure(true) and !$ev->isCancelled()){
@ -1710,30 +1695,26 @@ class Level implements ChunkManager, Metadatable{
* @return bool
*/
public function useItemOn(Vector3 $vector, Item &$item, int $face, Vector3 $facePos = null, Player $player = null, bool $playSound = false) : bool{
$target = $this->getBlock($vector);
$block = $target->getSide($face);
$blockClicked = $this->getBlock($vector);
$blockReplace = $blockClicked->getSide($face);
if($facePos === null){
$facePos = new Vector3(0.0, 0.0, 0.0);
}
if($block->y >= $this->provider->getWorldHeight() or $block->y < 0){
if($blockReplace->y >= $this->provider->getWorldHeight() or $blockReplace->y < 0){
//TODO: build height limit messages for custom world heights and mcregion cap
return false;
}
if($target->getId() === Item::AIR){
if($blockClicked->getId() === Item::AIR){
return false;
}
if($player !== null){
$ev = new PlayerInteractEvent($player, $item, $target, $face, $target->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK);
if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
$t = new Vector2($target->x, $target->z);
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
$ev->setCancelled();
}
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $face, $blockClicked->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK);
if($this->checkSpawnProtection($player, $blockClicked)){
$ev->setCancelled(); //set it to cancelled so plugins can bypass this
}
if($player->isAdventure(true) and !$ev->isCancelled()){
@ -1743,7 +1724,7 @@ class Level implements ChunkManager, Metadatable{
foreach($tag as $v){
if($v instanceof StringTag){
$entry = ItemFactory::fromString($v->getValue());
if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){
if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $blockClicked->getId()){
$canPlace = true;
break;
}
@ -1756,12 +1737,12 @@ class Level implements ChunkManager, Metadatable{
$this->server->getPluginManager()->callEvent($ev);
if(!$ev->isCancelled()){
$target->onUpdate(self::BLOCK_UPDATE_TOUCH);
if(!$player->isSneaking() and $target->onActivate($item, $player) === true){
$blockClicked->onUpdate(self::BLOCK_UPDATE_TOUCH);
if(!$player->isSneaking() and $blockClicked->onActivate($item, $player) === true){
return true;
}
if(!$player->isSneaking() and $item->onActivate($this, $player, $block, $target, $face, $facePos)){
if(!$player->isSneaking() and $item->onActivate($this, $player, $blockReplace, $blockClicked, $face, $facePos)){
if($item->getCount() <= 0){
$item = ItemFactory::get(Item::AIR, 0, 0);
@ -1771,7 +1752,7 @@ class Level implements ChunkManager, Metadatable{
}else{
return false;
}
}elseif($target->onActivate($item, $player) === true){
}elseif($blockClicked->onActivate($item, $player) === true){
return true;
}
@ -1781,13 +1762,13 @@ class Level implements ChunkManager, Metadatable{
$hand = $item->getBlock();
if($target->canBeReplaced($hand)){
$block = $target;
}elseif(!$block->canBeReplaced($hand)){
if($blockClicked->canBeReplaced($hand)){
$blockReplace = $blockClicked;
}elseif(!$blockReplace->canBeReplaced($hand)){
return false;
}
$hand->position($block);
$hand->position($blockReplace);
if($hand->isSolid() === true and $hand->getBoundingBox() !== null){
$entities = $this->getCollidingEntities($hand->getBoundingBox());
@ -1815,21 +1796,18 @@ class Level implements ChunkManager, Metadatable{
if($player !== null){
$ev = new BlockPlaceEvent($player, $hand, $block, $target, $item);
if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){
$t = new Vector2($target->x, $target->z);
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this
$ev->setCancelled();
}
$ev = new BlockPlaceEvent($player, $hand, $blockReplace, $blockClicked, $item);
if($this->checkSpawnProtection($player, $blockClicked)){
$ev->setCancelled();
}
$this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled()){
return false;
}
}
if(!$hand->place($item, $block, $target, $face, $facePos, $player)){
if(!$hand->place($item, $blockReplace, $blockClicked, $face, $facePos, $player)){
return false;
}