mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-22 19:06:35 +00:00
Merge branch 'minor-next' into stable
This commit is contained in:
commit
6f09286fed
@ -27,6 +27,7 @@ use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DripleafState;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
@ -145,6 +146,7 @@ $enumsUsed = [
|
||||
CopperOxidation::getAll(),
|
||||
CoralType::getAll(),
|
||||
DirtType::getAll(),
|
||||
DripleafState::getAll(),
|
||||
DyeColor::getAll(),
|
||||
FroglightType::getAll(),
|
||||
LeverFacing::getAll(),
|
||||
|
@ -32,7 +32,6 @@
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"fgrosse/phpasn1": "~2.5.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.1.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
|
||||
@ -50,7 +49,7 @@
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.2.0"
|
||||
"symfony/filesystem": "~6.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.16",
|
||||
|
90
composer.lock
generated
90
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e14717125dd180235fb888662a48846d",
|
||||
"content-hash": "ccd20e7656bc05ec2acd8e28aad9fcf2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -120,82 +120,6 @@
|
||||
],
|
||||
"time": "2023-01-15T23:15:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fgrosse/phpasn1",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fgrosse/PHPASN1.git",
|
||||
"reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b",
|
||||
"reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "~2.0",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "BCmath is the fallback extension for big integer calculations",
|
||||
"ext-curl": "For loading OID information from the web if they have not bee defined statically",
|
||||
"ext-gmp": "GMP is the preferred extension for big integer calculations",
|
||||
"phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FG\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Friedrich Große",
|
||||
"email": "friedrich.grosse@gmail.com",
|
||||
"homepage": "https://github.com/FGrosse",
|
||||
"role": "Author"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1",
|
||||
"keywords": [
|
||||
"DER",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"ber",
|
||||
"binary",
|
||||
"decoding",
|
||||
"encoding",
|
||||
"x.509",
|
||||
"x.690",
|
||||
"x509",
|
||||
"x690"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/fgrosse/PHPASN1/issues",
|
||||
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2022-12-19T11:08:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.1.0",
|
||||
@ -998,16 +922,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.2.12",
|
||||
"version": "v6.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "b0818e7203e53540f2a5c9a5017d97897df1e9bb"
|
||||
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b0818e7203e53540f2a5c9a5017d97897df1e9bb",
|
||||
"reference": "b0818e7203e53540f2a5c9a5017d97897df1e9bb",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
|
||||
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1041,7 +965,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.2.12"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1057,7 +981,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-01T08:29:37+00:00"
|
||||
"time": "2023-06-01T08:30:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
@ -93,6 +93,7 @@ use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
use pocketmine\thread\ThreadCrashException;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
@ -1009,7 +1010,7 @@ class Server{
|
||||
|
||||
$this->playerDataProvider = new DatFilePlayerDataProvider(Path::join($this->dataPath, "players"));
|
||||
|
||||
register_shutdown_function([$this, "crashDump"]);
|
||||
register_shutdown_function($this->crashDump(...));
|
||||
|
||||
$loadErrorCount = 0;
|
||||
$this->pluginManager->loadPlugins($this->pluginPath, $loadErrorCount);
|
||||
@ -1516,23 +1517,38 @@ class Server{
|
||||
$trace = $e->getTrace();
|
||||
}
|
||||
|
||||
$errstr = $e->getMessage();
|
||||
$errfile = $e->getFile();
|
||||
$errline = $e->getLine();
|
||||
//If this is a thread crash, this logs where the exception came from on the main thread, as opposed to the
|
||||
//crashed thread. This is intentional, and might be useful for debugging
|
||||
//Assume that the thread already logged the original exception with the correct stack trace
|
||||
$this->logger->logException($e, $trace);
|
||||
|
||||
if($e instanceof ThreadCrashException){
|
||||
$info = $e->getCrashInfo();
|
||||
$type = $info->getType();
|
||||
$errstr = $info->getMessage();
|
||||
$errfile = $info->getFile();
|
||||
$errline = $info->getLine();
|
||||
$printableTrace = $info->getTrace();
|
||||
$thread = $info->getThreadName();
|
||||
}else{
|
||||
$type = get_class($e);
|
||||
$errstr = $e->getMessage();
|
||||
$errfile = $e->getFile();
|
||||
$errline = $e->getLine();
|
||||
$printableTrace = Utils::printableTraceWithMetadata($trace);
|
||||
$thread = "Main";
|
||||
}
|
||||
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($errstr));
|
||||
|
||||
$errfile = Filesystem::cleanPath($errfile);
|
||||
|
||||
$this->logger->logException($e, $trace);
|
||||
|
||||
$lastError = [
|
||||
"type" => get_class($e),
|
||||
"type" => $type,
|
||||
"message" => $errstr,
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
"fullFile" => $errfile,
|
||||
"file" => Filesystem::cleanPath($errfile),
|
||||
"line" => $errline,
|
||||
"trace" => $trace
|
||||
"trace" => $printableTrace,
|
||||
"thread" => $thread
|
||||
];
|
||||
|
||||
global $lastExceptionError, $lastError;
|
||||
|
136
src/block/BaseBigDripleaf.php
Normal file
136
src/block/BaseBigDripleaf.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
abstract class BaseBigDripleaf extends Transparent{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
abstract protected function isHead() : bool;
|
||||
|
||||
private function canBeSupportedBy(Block $block, bool $head) : bool{
|
||||
//TODO: Moss block
|
||||
return
|
||||
($block instanceof BaseBigDripleaf && $block->isHead() === $head) ||
|
||||
$block->getTypeId() === BlockTypeIds::CLAY ||
|
||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(
|
||||
(!$this->isHead() && !$this->getSide(Facing::UP) instanceof BaseBigDripleaf) ||
|
||||
!$this->canBeSupportedBy($this->getSide(Facing::DOWN), false)
|
||||
){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$block = $blockReplace->getSide(Facing::DOWN);
|
||||
if(!$this->canBeSupportedBy($block, true)){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
if($block instanceof BaseBigDripleaf){
|
||||
$this->facing = $block->getFacing();
|
||||
$tx->addBlock($block->getPosition(), VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($this->facing));
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function seekToHead() : ?BaseBigDripleaf{
|
||||
if($this->isHead()){
|
||||
return $this;
|
||||
}
|
||||
$step = 1;
|
||||
while(($next = $this->getSide(Facing::UP, $step)) instanceof BaseBigDripleaf){
|
||||
if($next->isHead()){
|
||||
return $next;
|
||||
}
|
||||
$step++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : bool{
|
||||
$head = $this->seekToHead();
|
||||
if($head === null){
|
||||
return false;
|
||||
}
|
||||
$pos = $head->getPosition();
|
||||
$up = $pos->up();
|
||||
$world = $pos->getWorld();
|
||||
if(
|
||||
!$world->isInWorld($up->getFloorX(), $up->getFloorY(), $up->getFloorZ()) ||
|
||||
$world->getBlock($up)->getTypeId() !== BlockTypeIds::AIR
|
||||
){
|
||||
return false;
|
||||
}
|
||||
|
||||
$tx = new BlockTransaction($world);
|
||||
|
||||
$tx->addBlock($pos, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($head->getFacing()));
|
||||
$tx->addBlock($up, VanillaBlocks::BIG_DRIPLEAF_HEAD()->setFacing($head->getFacing()));
|
||||
|
||||
$ev = new StructureGrowEvent($head, $tx, $player);
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
return $tx->apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
return 15;
|
||||
}
|
||||
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ use function in_array;
|
||||
abstract class BaseRail extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
if($blockReplace->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport()){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ abstract class BaseRail extends Flowable{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
if(!$this->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport()){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach($this->getCurrentShapeConnections() as $connection){
|
||||
|
@ -177,11 +177,11 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
$this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH;
|
||||
|
||||
$next = $this->getSide($this->getOtherHalfSide());
|
||||
if($next->canBeReplaced() && $this->canBeSupportedBy($next->getSide(Facing::DOWN))){
|
||||
if($next->canBeReplaced() && $this->canBeSupportedAt($next)){
|
||||
$nextState = clone $this;
|
||||
$nextState->head = true;
|
||||
$tx->addBlock($blockReplace->position, $this)->addBlock($next->position, $nextState);
|
||||
@ -208,8 +208,8 @@ class Bed extends Transparent{
|
||||
return parent::getAffectedBlocks();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{ return 1; }
|
||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
class Beetroot extends Crops{
|
||||
|
||||
@ -33,7 +33,7 @@ class Beetroot extends Crops{
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return [
|
||||
VanillaItems::BEETROOT(),
|
||||
VanillaItems::BEETROOT_SEEDS()->setCount(mt_rand(0, 3))
|
||||
VanillaItems::BEETROOT_SEEDS()->setCount(FortuneDropHelper::binomial($item, 0))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BellRingSound;
|
||||
|
||||
@ -87,46 +88,44 @@ final class Bell extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return !$block->getSupportType($face)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
return false;
|
||||
}
|
||||
if($face === Facing::UP){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()), Facing::UP)){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
|
||||
}
|
||||
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
||||
}elseif($face === Facing::DOWN){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()), Facing::DOWN)){
|
||||
return false;
|
||||
}
|
||||
$this->setAttachmentType(BellAttachmentType::CEILING());
|
||||
}else{
|
||||
$this->setFacing($face);
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))), $face)){
|
||||
$this->setAttachmentType(BellAttachmentType::ONE_WALL());
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)), Facing::opposite($face))){
|
||||
$this->setAttachmentType(BellAttachmentType::TWO_WALLS());
|
||||
}
|
||||
$this->setAttachmentType(
|
||||
$this->canBeSupportedAt($blockReplace, $face) ?
|
||||
BellAttachmentType::TWO_WALLS() :
|
||||
BellAttachmentType::ONE_WALL()
|
||||
);
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(
|
||||
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP), Facing::DOWN)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN), Facing::UP)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing), Facing::opposite($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)))
|
||||
){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
foreach(match($this->attachmentType){
|
||||
BellAttachmentType::CEILING() => [Facing::UP],
|
||||
BellAttachmentType::FLOOR() => [Facing::DOWN],
|
||||
BellAttachmentType::ONE_WALL() => [Facing::opposite($this->facing)],
|
||||
BellAttachmentType::TWO_WALLS() => [$this->facing, Facing::opposite($this->facing)],
|
||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
||||
} as $supportBlockDirection){
|
||||
if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,13 +158,11 @@ final class Bell extends Transparent{
|
||||
}
|
||||
|
||||
private function isValidFaceToRing(int $faceHit) : bool{
|
||||
return (
|
||||
$this->attachmentType->equals(BellAttachmentType::CEILING()) ||
|
||||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)) ||
|
||||
(
|
||||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
|
||||
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
|
||||
)
|
||||
);
|
||||
return match($this->attachmentType){
|
||||
BellAttachmentType::CEILING() => true,
|
||||
BellAttachmentType::FLOOR() => Facing::axis($faceHit) === Facing::axis($this->facing),
|
||||
BellAttachmentType::ONE_WALL(), BellAttachmentType::TWO_WALLS() => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
|
||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
132
src/block/BigDripleafHead.php
Normal file
132
src/block/BigDripleafHead.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\DripleafState;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\sound\DripleafTiltDownSound;
|
||||
use pocketmine\world\sound\DripleafTiltUpSound;
|
||||
|
||||
class BigDripleafHead extends BaseBigDripleaf{
|
||||
|
||||
protected DripleafState $leafState;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->leafState = DripleafState::STABLE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
parent::describeBlockOnlyState($w);
|
||||
$w->dripleafState($this->leafState);
|
||||
}
|
||||
|
||||
protected function isHead() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLeafState() : DripleafState{
|
||||
return $this->leafState;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setLeafState(DripleafState $leafState) : self{
|
||||
$this->leafState = $leafState;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function setTiltAndScheduleTick(DripleafState $tilt) : void{
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLeafState($tilt));
|
||||
$delay = $tilt->getScheduledUpdateDelayTicks();
|
||||
if($delay !== null){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, $delay);
|
||||
}
|
||||
}
|
||||
|
||||
private function getLeafTopOffset() : float{
|
||||
return match($this->leafState){
|
||||
DripleafState::STABLE(), DripleafState::UNSTABLE() => 1 / 16,
|
||||
DripleafState::PARTIAL_TILT() => 3 / 16,
|
||||
default => 0
|
||||
};
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if(!$entity instanceof Projectile && $this->leafState->equals(DripleafState::STABLE())){
|
||||
//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
|
||||
$intersection = AxisAlignedBB::one()
|
||||
->offset($this->position->x, $this->position->y, $this->position->z)
|
||||
->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
|
||||
if($entity->getBoundingBox()->intersectsWith($intersection)){
|
||||
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT());
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||
}
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if(!$this->leafState->equals(DripleafState::STABLE())){
|
||||
if($this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE()));
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
|
||||
}else{
|
||||
$this->setTiltAndScheduleTick(match($this->leafState->id()){
|
||||
DripleafState::UNSTABLE()->id() => DripleafState::PARTIAL_TILT(),
|
||||
DripleafState::PARTIAL_TILT()->id() => DripleafState::FULL_TILT(),
|
||||
default => throw new AssumptionFailedError("All types should be covered")
|
||||
});
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
->trim(Facing::DOWN, 11 / 16)
|
||||
->trim(Facing::UP, $this->getLeafTopOffset())
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
41
src/block/BigDripleafStem.php
Normal file
41
src/block/BigDripleafStem.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class BigDripleafStem extends BaseBigDripleaf{
|
||||
|
||||
protected function isHead() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
return VanillaBlocks::BIG_DRIPLEAF_HEAD()->asItem();
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -863,6 +864,10 @@ class Block{
|
||||
return SupportType::FULL();
|
||||
}
|
||||
|
||||
protected function getAdjacentSupportType(int $facing) : SupportType{
|
||||
return $this->getSide($facing)->getSupportType(Facing::opposite($facing));
|
||||
}
|
||||
|
||||
public function isFullCube() : bool{
|
||||
$bb = $this->getCollisionBoxes();
|
||||
|
||||
|
@ -733,8 +733,11 @@ final class BlockTypeIds{
|
||||
public const CHERRY_TRAPDOOR = 10703;
|
||||
public const CHERRY_WALL_SIGN = 10704;
|
||||
public const CHERRY_WOOD = 10705;
|
||||
public const SMALL_DRIPLEAF = 10706;
|
||||
public const BIG_DRIPLEAF_HEAD = 10707;
|
||||
public const BIG_DRIPLEAF_STEM = 10708;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10706;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10709;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -52,7 +52,7 @@ abstract class Button extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
if($this->canBeSupportedAt($blockReplace, $face)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -83,12 +83,12 @@ abstract class Button extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){
|
||||
if(!$this->canBeSupportedAt($this, $this->facing)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $support, int $face) : bool{
|
||||
return $support->getSupportType($face)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::opposite($face))->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +104,7 @@ class Candle extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $blockReplace->getSide(Facing::DOWN);
|
||||
if(!$down->getSupportType(Facing::UP)->hasCenterSupport()){
|
||||
if(!$blockReplace->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport()){
|
||||
return false;
|
||||
}
|
||||
$existing = $this->getCandleIfCompatibleType($blockReplace);
|
||||
|
@ -23,15 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
class Carrot extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::CARROT()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 4) : 1)
|
||||
VanillaItems::CARROT()->setCount($this->age >= self::MAX_AGE ? FortuneDropHelper::binomial($item, 1) : 1)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
|
||||
class CarvedPumpkin extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
}
|
||||
|
@ -87,18 +87,18 @@ class CaveVines extends Flowable{
|
||||
return $this->berries ? 14 : 0;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::DOWN)->equals(SupportType::FULL()) || $block->hasSameTypeId($this);
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL()) || $block->hasSameTypeId($this);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::UP))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::UP))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
$this->age = mt_rand(0, self::MAX_AGE);
|
||||
|
@ -24,14 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class ChemistryTable extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
//TODO
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\ChestPairEvent;
|
||||
use pocketmine\item\Item;
|
||||
@ -36,7 +35,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class Chest extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,7 @@ class CoalOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::COAL()
|
||||
VanillaItems::COAL()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -23,13 +23,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
|
||||
final class CopperOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [VanillaItems::RAW_COPPER()];
|
||||
return [
|
||||
VanillaItems::RAW_COPPER()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 5)),
|
||||
];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{ return true; }
|
||||
|
@ -32,7 +32,7 @@ use pocketmine\world\BlockTransaction;
|
||||
final class Coral extends BaseCoral{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
@ -40,14 +40,14 @@ final class Coral extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,7 @@ class DiamondOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::DIAMOND()
|
||||
VanillaItems::DIAMOND()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ class Door extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN)) && !$this->getSide(Facing::DOWN) instanceof Door){ //Replace with common break method
|
||||
if(!$this->canBeSupportedAt($this) && !$this->getSide(Facing::DOWN) instanceof Door){ //Replace with common break method
|
||||
$this->position->getWorld()->useBreakOn($this->position); //this will delete both halves if they exist
|
||||
}
|
||||
}
|
||||
@ -114,8 +114,7 @@ class Door extends Transparent{
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face === Facing::UP){
|
||||
$blockUp = $this->getSide(Facing::UP);
|
||||
$blockDown = $this->getSide(Facing::DOWN);
|
||||
if(!$blockUp->canBeReplaced() || !$this->canBeSupportedBy($blockDown)){
|
||||
if(!$blockUp->canBeReplaced() || !$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -172,7 +171,7 @@ class Door extends Transparent{
|
||||
return parent::getAffectedBlocks();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasEdgeSupport();
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport();
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
class DoubleTallGrass extends DoublePlant{
|
||||
|
||||
@ -34,8 +33,8 @@ class DoubleTallGrass extends DoublePlant{
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
if($this->top && mt_rand(0, 7) === 0){
|
||||
return [VanillaItems::WHEAT_SEEDS()];
|
||||
if($this->top){
|
||||
return FortuneDropHelper::grass($item);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,7 @@ class EmeraldOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::EMERALD()
|
||||
VanillaItems::EMERALD()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class EndPortalFrame extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected bool $eye = false;
|
||||
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\inventory\EnderChestInventory;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -36,7 +35,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class EnderChest extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 7;
|
||||
|
@ -53,7 +53,7 @@ final class FloorCoralFan extends BaseCoral{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
@ -75,15 +75,15 @@ final class FloorCoralFan extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
|
@ -90,7 +90,7 @@ class FlowerPot extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -98,13 +98,13 @@ class FlowerPot extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Furnace as TileFurnace;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -35,7 +34,6 @@ use function mt_rand;
|
||||
|
||||
class Furnace extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected FurnaceType $furnaceType;
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -30,7 +31,7 @@ use function mt_rand;
|
||||
final class GildedBlackstone extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(mt_rand(1, 10) === 1){
|
||||
if(FortuneDropHelper::bonusChanceDivisor($item, 10, 3)){
|
||||
return [VanillaItems::GOLD_NUGGET()->setCount(mt_rand(2, 5))];
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,10 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
|
||||
class GlazedTerracotta extends Opaque{
|
||||
use ColoredTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::BLACK();
|
||||
|
@ -121,7 +121,7 @@ class GlowLichen extends Transparent{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if(!$this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
|
||||
if(!$this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
@ -275,7 +275,7 @@ class GlowLichen extends Transparent{
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
use function min;
|
||||
|
||||
class Glowstone extends Transparent{
|
||||
|
||||
@ -35,7 +36,7 @@ class Glowstone extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::GLOWSTONE_DUST()->setCount(mt_rand(2, 4))
|
||||
VanillaItems::GLOWSTONE_DUST()->setCount(min(4, FortuneDropHelper::discrete($item, 2, 4)))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,15 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
class Gravel extends Opaque implements Fallable{
|
||||
use FallableTrait;
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(mt_rand(1, 10) === 1){
|
||||
if(FortuneDropHelper::bonusChanceDivisor($item, 10, 3)){
|
||||
return [
|
||||
VanillaItems::FLINT()
|
||||
];
|
||||
|
@ -163,18 +163,18 @@ class ItemFrame extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return !$block->getSupportType($face)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face) && Facing::axis($face) !== Axis::Y){
|
||||
if($this->canBeSupportedAt($blockReplace, Facing::opposite($face)) && Facing::axis($face) !== Axis::Y){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -79,12 +79,12 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){ //Replace with common break method
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){ //Replace with common break method
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->equals(SupportType::FULL());
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType($face)->equals(SupportType::FULL());
|
||||
}
|
||||
}
|
||||
|
@ -77,22 +77,23 @@ class Lantern extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::UP), Facing::DOWN) && !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN), Facing::UP)){
|
||||
$downSupport = $this->canBeSupportedAt($blockReplace, Facing::DOWN);
|
||||
if(!$downSupport && !$this->canBeSupportedAt($blockReplace, Facing::UP)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->hanging = ($face === Facing::DOWN || !$this->canBeSupportedBy($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()), Facing::UP));
|
||||
$this->hanging = $face === Facing::DOWN || !$downSupport;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$face = $this->hanging ? Facing::UP : Facing::DOWN;
|
||||
if(!$this->canBeSupportedBy($this->getSide($face), Facing::opposite($face))){
|
||||
if(!$this->canBeSupportedAt($this, $face)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,7 @@ class LapisOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::LAPIS_LAZULI()->setCount(mt_rand(4, 8))
|
||||
VanillaItems::LAPIS_LAZULI()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 9))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\block\utils\LeavesType;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -138,7 +139,8 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
$drops = [];
|
||||
if(mt_rand(1, 20) === 1){ //Saplings
|
||||
if(FortuneDropHelper::bonusChanceDivisor($item, 20, 4)){ //Saplings
|
||||
// TODO: according to the wiki, the jungle saplings have a different drop rate
|
||||
$sapling = (match($this->leavesType){
|
||||
LeavesType::ACACIA() => VanillaBlocks::ACACIA_SAPLING(),
|
||||
LeavesType::BIRCH() => VanillaBlocks::BIRCH_SAPLING(),
|
||||
@ -155,10 +157,13 @@ class Leaves extends Transparent{
|
||||
$drops[] = $sapling;
|
||||
}
|
||||
}
|
||||
if(($this->leavesType->equals(LeavesType::OAK()) || $this->leavesType->equals(LeavesType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
|
||||
if(
|
||||
($this->leavesType->equals(LeavesType::OAK()) || $this->leavesType->equals(LeavesType::DARK_OAK())) &&
|
||||
FortuneDropHelper::bonusChanceDivisor($item, 200, 20)
|
||||
){ //Apples
|
||||
$drops[] = VanillaItems::APPLE();
|
||||
}
|
||||
if(mt_rand(1, 50) === 1){
|
||||
if(FortuneDropHelper::bonusChanceDivisor($item, 50, 5)){
|
||||
$drops[] = VanillaItems::STICK()->setCount(mt_rand(1, 2));
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -39,7 +38,6 @@ use function count;
|
||||
|
||||
class Lectern extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected int $viewedPage = 0;
|
||||
protected ?WritableBookBase $book = null;
|
||||
|
@ -66,7 +66,7 @@ class Lever extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -90,8 +90,7 @@ class Lever extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$facing = $this->facing->getFacing();
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($facing)), $facing)){
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing->getFacing()))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
@ -107,8 +106,8 @@ class Lever extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
@ -312,7 +312,7 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
if($adjacentDecay <= self::MAX_DECAY){
|
||||
$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), \Closure::fromCallable([$this, 'canFlowInto']));
|
||||
$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), $this->canFlowInto(...));
|
||||
foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
|
||||
$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
|
||||
}
|
||||
|
@ -25,14 +25,12 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\LoomInventory;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class Loom extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
|
@ -23,15 +23,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
use function min;
|
||||
|
||||
class Melon extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::MELON()->setCount(mt_rand(3, 7))
|
||||
VanillaItems::MELON()->setCount(min(9, FortuneDropHelper::discrete($item, 3, 7)))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -23,14 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
final class NetherGoldOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [VanillaItems::GOLD_NUGGET()->setCount(mt_rand(2, 6))];
|
||||
return [VanillaItems::GOLD_NUGGET()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 6))];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{ return true; }
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,7 @@ class NetherQuartzOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::NETHER_QUARTZ()
|
||||
VanillaItems::NETHER_QUARTZ()->setCount(FortuneDropHelper::weighted($item, min: 1, maxBase: 1))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
@ -82,16 +83,13 @@ class NetherVines extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getSupportFace() : int{
|
||||
return Facing::opposite($this->growthFace);
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType($this->getSupportFace())->hasCenterSupport() || $block->hasSameTypeId($this);
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::opposite($this->growthFace));
|
||||
return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide($this->getSupportFace()))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
@ -108,7 +106,7 @@ class NetherVines extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportFace()))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
$this->age = mt_rand(0, self::MAX_AGE - 1);
|
||||
@ -179,7 +177,7 @@ class NetherVines extends Flowable{
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(($item->getBlockToolType() & BlockToolType::SHEARS) !== 0 || mt_rand(1, 3) === 1){
|
||||
if(($item->getBlockToolType() & BlockToolType::SHEARS) !== 0 || FortuneDropHelper::bonusChanceFixed($item, 1 / 3, 2 / 9)){
|
||||
return [$this->asItem()];
|
||||
}
|
||||
return [];
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
@ -85,7 +86,7 @@ class NetherWartPlant extends Flowable{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
$this->asItem()->setCount($this->age === self::MAX_AGE ? mt_rand(2, 4) : 1)
|
||||
$this->asItem()->setCount($this->age === self::MAX_AGE ? FortuneDropHelper::discrete($item, 2, 4) : 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
@ -31,7 +32,8 @@ class Potato extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$result = [
|
||||
VanillaItems::POTATO()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 5) : 1)
|
||||
//min/max would be 2-5 in Java
|
||||
VanillaItems::POTATO()->setCount($this->age >= self::MAX_AGE ? FortuneDropHelper::binomial($item, 1) : 1)
|
||||
];
|
||||
if($this->age >= self::MAX_AGE && mt_rand(0, 49) === 0){
|
||||
$result[] = VanillaItems::POISONOUS_POTATO();
|
||||
|
@ -45,18 +45,18 @@ abstract class PressurePlate extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class RedstoneComparator extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
@ -102,13 +102,13 @@ class RedstoneComparator extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
@ -81,7 +82,7 @@ class RedstoneOre extends Opaque{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::REDSTONE_DUST()->setCount(mt_rand(4, 5))
|
||||
VanillaItems::REDSTONE_DUST()->setCount(FortuneDropHelper::discrete($item, 4, 5))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ class RedstoneRepeater extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
@ -88,13 +88,13 @@ class RedstoneRepeater extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
|
@ -35,7 +35,7 @@ class RedstoneWire extends Flowable{
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
@ -49,13 +49,13 @@ class RedstoneWire extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
|
@ -23,8 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function min;
|
||||
|
||||
class SeaLantern extends Transparent{
|
||||
|
||||
@ -34,7 +36,7 @@ class SeaLantern extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::PRISMARINE_CRYSTALS()->setCount(3)
|
||||
VanillaItems::PRISMARINE_CRYSTALS()->setCount(min(5, FortuneDropHelper::discrete($item, 2, 3)))
|
||||
];
|
||||
}
|
||||
|
||||
|
170
src/block/SmallDripleaf.php
Normal file
170
src/block/SmallDripleaf.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
use function mt_rand;
|
||||
|
||||
class SmallDripleaf extends Transparent{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected bool $top = false;
|
||||
|
||||
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->top);
|
||||
}
|
||||
|
||||
public function isTop() : bool{
|
||||
return $this->top;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setTop(bool $top) : self{
|
||||
$this->top = $top;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
//TODO: Moss
|
||||
//TODO: Small Dripleaf also can be placed on dirt, coarse dirt, farmland, grass blocks,
|
||||
// podzol, rooted dirt, mycelium, and mud if these blocks are underwater (needs waterlogging)
|
||||
return $block->getTypeId() === BlockTypeIds::CLAY;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->top && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
return;
|
||||
}
|
||||
$face = $this->top ? Facing::DOWN : Facing::UP;
|
||||
if(!$this->getSide($face)->hasSameTypeId($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$block = $blockReplace->getSide(Facing::UP);
|
||||
if($block->getTypeId() !== BlockTypeIds::AIR || !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
||||
$tx->addBlock($block->getPosition(), VanillaBlocks::SMALL_DRIPLEAF()
|
||||
->setFacing($this->facing)
|
||||
->setTop(true)
|
||||
);
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canGrowTo(Position $pos) : bool{
|
||||
$world = $pos->getWorld();
|
||||
if(!$world->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ())){
|
||||
return false;
|
||||
}
|
||||
$block = $world->getBlock($pos);
|
||||
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::AIR;
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : bool{
|
||||
$bottomBlock = $this->top ? $this->getSide(Facing::DOWN) : $this;
|
||||
if(!$this->hasSameTypeId($bottomBlock)){
|
||||
return false;
|
||||
}
|
||||
$world = $this->position->getWorld();
|
||||
$tx = new BlockTransaction($world);
|
||||
$height = mt_rand(2, 5);
|
||||
$grown = 0;
|
||||
for($i = 0; $i < $height; $i++){
|
||||
$pos = $bottomBlock->getSide(Facing::UP, $i)->getPosition();
|
||||
if(!$this->canGrowTo($pos)){
|
||||
break;
|
||||
}
|
||||
$block = ++$grown < $height && $this->canGrowTo($pos->getSide(Facing::UP)) ?
|
||||
VanillaBlocks::BIG_DRIPLEAF_STEM() :
|
||||
VanillaBlocks::BIG_DRIPLEAF_HEAD();
|
||||
$tx->addBlock($pos, $block->setFacing($this->facing));
|
||||
}
|
||||
if($grown > 1){
|
||||
$ev = new StructureGrowEvent($bottomBlock, $tx, $player);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
return $tx->apply();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAffectedBlocks() : array{
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
if($other->hasSameTypeId($this)){
|
||||
return [$this, $other];
|
||||
}
|
||||
return parent::getAffectedBlocks();
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(!$this->top){
|
||||
return [$this->asItem()];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
return 15;
|
||||
}
|
||||
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -80,8 +80,8 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $b) : bool{
|
||||
return $b->getSupportType(Facing::UP)->equals(SupportType::FULL());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::FULL());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -91,7 +91,7 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
}
|
||||
$this->layers = $blockReplace->layers + 1;
|
||||
}
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,12 @@ use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class SporeBlossom extends Flowable{
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::DOWN)->equals(SupportType::FULL());
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::UP))){
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ final class SporeBlossom extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::UP))){
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
@ -30,11 +31,35 @@ use function array_rand;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Stem extends Crops{
|
||||
protected int $facing = Facing::UP;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
parent::describeBlockOnlyState($w);
|
||||
$w->facingExcept($this->facing, Facing::DOWN);
|
||||
}
|
||||
|
||||
public function getFacing() : int{ return $this->facing; }
|
||||
|
||||
/** @return $this */
|
||||
public function setFacing(int $facing) : self{
|
||||
if($facing === Facing::DOWN){
|
||||
throw new \InvalidArgumentException("DOWN is not a valid facing for this block");
|
||||
}
|
||||
$this->facing = $facing;
|
||||
return $this;
|
||||
}
|
||||
|
||||
abstract protected function getPlant() : Block;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->facing !== Facing::UP && !$this->getSide($this->facing)->hasSameTypeId($this->getPlant())){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setFacing(Facing::UP));
|
||||
}
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(0, 2) === 1){
|
||||
if($this->facing === Facing::UP && mt_rand(0, 2) === 1){
|
||||
$world = $this->position->getWorld();
|
||||
if($this->age < self::MAX_AGE){
|
||||
$block = clone $this;
|
||||
@ -52,11 +77,13 @@ abstract class Stem extends Crops{
|
||||
}
|
||||
}
|
||||
|
||||
$side = $this->getSide(Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]);
|
||||
$facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)];
|
||||
$side = $this->getSide($facing);
|
||||
if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){
|
||||
$ev = new BlockGrowEvent($side, $grow);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$world->setBlock($this->position, $this->setFacing($facing));
|
||||
$world->setBlock($side->position, $ev->getNewState());
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\StonecutterInventory;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -35,7 +34,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class Stonecutter extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
@ -108,13 +109,14 @@ class SweetBerryBush extends Flowable{
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if(($dropAmount = $this->getBerryDropAmount()) > 0){
|
||||
return [
|
||||
$this->asItem()->setCount($dropAmount)
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
$count = match($this->age){
|
||||
self::STAGE_MATURE => FortuneDropHelper::discrete($item, 2, 3),
|
||||
self::STAGE_BUSH_SOME_BERRIES => FortuneDropHelper::discrete($item, 1, 2),
|
||||
default => 0
|
||||
};
|
||||
return [
|
||||
$this->asItem()->setCount($count)
|
||||
];
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -23,13 +23,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class TallGrass extends Flowable{
|
||||
|
||||
@ -56,13 +55,7 @@ class TallGrass extends Flowable{
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
if(mt_rand(0, 15) === 0){
|
||||
return [
|
||||
VanillaItems::WHEAT_SEEDS()
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
return FortuneDropHelper::grass($item);
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
@ -56,15 +55,13 @@ class Torch extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$face = Facing::opposite($this->facing);
|
||||
|
||||
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
if($face !== Facing::DOWN && $this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}else{
|
||||
@ -75,8 +72,7 @@ class Torch extends Flowable{
|
||||
Facing::EAST,
|
||||
Facing::DOWN
|
||||
] as $side){
|
||||
$block = $this->getSide($side);
|
||||
if($this->canBeSupportedBy($block, Facing::opposite($side))){
|
||||
if($this->canBeSupportedAt($blockReplace, $side)){
|
||||
$this->facing = Facing::opposite($side);
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -85,8 +81,9 @@ class Torch extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $support, int $face) : bool{
|
||||
return ($face === Facing::UP && $support->getSupportType($face)->hasCenterSupport()) ||
|
||||
(Facing::axis($face) !== Axis::Y && $support->getSupportType($face)->equals(SupportType::FULL()));
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $face === Facing::DOWN ?
|
||||
$block->getAdjacentSupportType($face)->hasCenterSupport() :
|
||||
$block->getAdjacentSupportType($face)->equals(SupportType::FULL());
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,8 @@ use function mb_strtolower;
|
||||
* @method static Bedrock BEDROCK()
|
||||
* @method static Beetroot BEETROOTS()
|
||||
* @method static Bell BELL()
|
||||
* @method static BigDripleafHead BIG_DRIPLEAF_HEAD()
|
||||
* @method static BigDripleafStem BIG_DRIPLEAF_STEM()
|
||||
* @method static WoodenButton BIRCH_BUTTON()
|
||||
* @method static WoodenDoor BIRCH_DOOR()
|
||||
* @method static WoodenFence BIRCH_FENCE()
|
||||
@ -658,6 +660,7 @@ use function mb_strtolower;
|
||||
* @method static Opaque SHROOMLIGHT()
|
||||
* @method static ShulkerBox SHULKER_BOX()
|
||||
* @method static Slime SLIME()
|
||||
* @method static SmallDripleaf SMALL_DRIPLEAF()
|
||||
* @method static SmithingTable SMITHING_TABLE()
|
||||
* @method static Furnace SMOKER()
|
||||
* @method static Opaque SMOOTH_BASALT()
|
||||
@ -1598,6 +1601,10 @@ final class VanillaBlocks{
|
||||
self::register("hanging_roots", new HangingRoots(new BID(Ids::HANGING_ROOTS), "Hanging Roots", new Info(BreakInfo::instant(ToolType::SHEARS, 1))));
|
||||
|
||||
self::register("cave_vines", new CaveVines(new BID(Ids::CAVE_VINES), "Cave Vines", new Info(BreakInfo::instant())));
|
||||
|
||||
self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(BlockToolType::SHEARS, toolHarvestLevel: 1))));
|
||||
self::register("big_dripleaf_head", new BigDripleafHead(new BID(Ids::BIG_DRIPLEAF_HEAD), "Big Dripleaf", new Info(BreakInfo::instant())));
|
||||
self::register("big_dripleaf_stem", new BigDripleafStem(new BID(Ids::BIG_DRIPLEAF_STEM), "Big Dripleaf Stem", new Info(BreakInfo::instant())));
|
||||
}
|
||||
|
||||
private static function registerBlocksR18() : void{
|
||||
|
@ -42,7 +42,7 @@ final class WallCoralFan extends BaseCoral{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$axis = Facing::axis($face);
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
return false;
|
||||
}
|
||||
$this->facing = $face;
|
||||
@ -54,15 +54,15 @@ final class WallCoralFan extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->getSide(Facing::opposite($this->facing))), $this->facing)){
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function mt_rand;
|
||||
|
||||
class Wheat extends Crops{
|
||||
|
||||
@ -33,7 +33,7 @@ class Wheat extends Crops{
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return [
|
||||
VanillaItems::WHEAT(),
|
||||
VanillaItems::WHEAT_SEEDS()->setCount(mt_rand(0, 3))
|
||||
VanillaItems::WHEAT_SEEDS()->setCount(FortuneDropHelper::binomial($item, 0))
|
||||
];
|
||||
}else{
|
||||
return [
|
||||
|
65
src/block/utils/DripleafState.php
Normal file
65
src/block/utils/DripleafState.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?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\block\utils;
|
||||
|
||||
use pocketmine\utils\EnumTrait;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
* This must be regenerated whenever registry members are added, removed or changed.
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static DripleafState FULL_TILT()
|
||||
* @method static DripleafState PARTIAL_TILT()
|
||||
* @method static DripleafState STABLE()
|
||||
* @method static DripleafState UNSTABLE()
|
||||
*/
|
||||
final class DripleafState{
|
||||
use EnumTrait {
|
||||
register as Enum_register;
|
||||
__construct as Enum___construct;
|
||||
}
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new self("stable", null),
|
||||
new self("unstable", 10),
|
||||
new self("partial_tilt", 10),
|
||||
new self("full_tilt", 100)
|
||||
);
|
||||
}
|
||||
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private ?int $scheduledUpdateDelayTicks
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
}
|
||||
|
||||
public function getScheduledUpdateDelayTicks() : ?int{
|
||||
return $this->scheduledUpdateDelayTicks;
|
||||
}
|
||||
|
||||
}
|
150
src/block/utils/FortuneDropHelper.php
Normal file
150
src/block/utils/FortuneDropHelper.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?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\block\utils;
|
||||
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use function max;
|
||||
use function min;
|
||||
use function mt_getrandmax;
|
||||
use function mt_rand;
|
||||
|
||||
final class FortuneDropHelper{
|
||||
/**
|
||||
* If a random number between 0-1 is greater than 2/(level+2), this multiplies the max drop amount by level+1, and
|
||||
* picks a random amount between the minimum and multiplied maximum. Each level of fortune increases the chance of
|
||||
* fortune activation, and also increases the maximum drop limit when activated.
|
||||
*
|
||||
* Otherwise, returns a random amount of the item between the minimum and original maximum.
|
||||
*
|
||||
* @param Item $usedItem The item used to break the block
|
||||
* @param int $min Minimum amount
|
||||
* @param int $maxBase Maximum amount, as if fortune level was 0
|
||||
*
|
||||
* @return int the number of items to drop
|
||||
*/
|
||||
public static function weighted(Item $usedItem, int $min, int $maxBase) : int{
|
||||
if($maxBase < $min){
|
||||
throw new \InvalidArgumentException("Maximum drop amount must be greater than or equal to minimum drop amount");
|
||||
}
|
||||
|
||||
$fortuneLevel = $usedItem->getEnchantmentLevel(VanillaEnchantments::FORTUNE());
|
||||
|
||||
return mt_rand($min,
|
||||
$fortuneLevel > 0 && mt_rand() / mt_getrandmax() > 2 / ($fortuneLevel + 2) ?
|
||||
$maxBase * ($fortuneLevel + 1) :
|
||||
$maxBase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the drop amount according to a binomial distribution. The function will roll maxBase+level times, and add 1
|
||||
* if a random number between 0-1 is less than the given probability. Each level of fortune adds one extra roll.
|
||||
*
|
||||
* As many as maxBase+level items can be dropped. This applies even if the fortune level is 0.
|
||||
*
|
||||
* @param float $chance The chance of adding 1 to the amount for each roll, must be in the range 0-1
|
||||
* @param int $min Minimum amount
|
||||
* @param int $minRolls Number of rolls if fortune level is 0, added to fortune level to calculate total rolls
|
||||
*
|
||||
* @return int the number of items to drop
|
||||
*/
|
||||
public static function binomial(Item $usedItem, int $min, int $minRolls = 3, float $chance = 4 / 7) : int{
|
||||
$fortuneLevel = $usedItem->getEnchantmentLevel(VanillaEnchantments::FORTUNE());
|
||||
|
||||
$count = $min;
|
||||
$rolls = $minRolls + $fortuneLevel;
|
||||
for($i = 0; $i < $rolls; ++$i){
|
||||
if(mt_rand() / mt_getrandmax() < $chance){
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grass have a fixed chance to drop wheat seed.
|
||||
* Fortune level increases the maximum number of seeds that can be dropped.
|
||||
* A discrete uniform distribution is used to determine the number of seeds dropped.
|
||||
*
|
||||
* TODO: I'm not sure this really belongs here, but it's preferable not to duplicate this code between grass and
|
||||
* tall grass.
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public static function grass(Item $usedItem) : array{
|
||||
if(FortuneDropHelper::bonusChanceDivisor($usedItem, 8, 2)){
|
||||
return [
|
||||
VanillaItems::WHEAT_SEEDS()
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the fortune level to the base max and picks a random number between the minimim and adjusted maximum.
|
||||
* Each amount in the range has an equal chance of being picked.
|
||||
*
|
||||
* @param int $maxBase Maximum base amount, as if the fortune level was 0
|
||||
*
|
||||
* @return int the number of items to drop
|
||||
*/
|
||||
public static function discrete(Item $usedItem, int $min, int $maxBase) : int{
|
||||
if($maxBase < $min){
|
||||
throw new \InvalidArgumentException("Minimum base drop amount must be less than or equal to maximum base drop amount");
|
||||
}
|
||||
|
||||
$max = $maxBase + $usedItem->getEnchantmentLevel(VanillaEnchantments::FORTUNE());
|
||||
return mt_rand($min, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a chance of getting an extra bonus drop by reducing the chance divisor by a given amount per fortune
|
||||
* level.
|
||||
*
|
||||
* @param int $divisorBase The number to divide 1 by to get the chance, as if the fortune level was 0
|
||||
* @param int $divisorSubtractPerLevel The amount to subtract from the divisor for each level of fortune
|
||||
*
|
||||
* @return bool whether the bonus drop should be added
|
||||
*/
|
||||
public static function bonusChanceDivisor(Item $usedItem, int $divisorBase, int $divisorSubtractPerLevel) : bool{
|
||||
$fortuneLevel = $usedItem->getEnchantmentLevel(VanillaEnchantments::FORTUNE());
|
||||
return mt_rand(1, max(1, $divisorBase - ($fortuneLevel * $divisorSubtractPerLevel))) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a chance of getting an extra bonus drop by increasing the chance by a fixed amount per fortune level.
|
||||
*
|
||||
* @param float $chanceBase The base chance of getting a bonus drop, as if the fortune level was 0
|
||||
* @param float $addedChancePerLevel The amount to add to the chance for each level of fortune
|
||||
*/
|
||||
public static function bonusChanceFixed(Item $usedItem, float $chanceBase, float $addedChancePerLevel) : bool{
|
||||
$fortuneLevel = $usedItem->getEnchantmentLevel(VanillaEnchantments::FORTUNE());
|
||||
$chance = min(1, $chanceBase + ($fortuneLevel * $addedChancePerLevel));
|
||||
return mt_rand() / mt_getrandmax() < $chance;
|
||||
}
|
||||
}
|
@ -209,9 +209,6 @@ final class CraftingManagerFromDataHelper{
|
||||
public static function make(string $directoryPath) : CraftingManager{
|
||||
$result = new CraftingManager();
|
||||
|
||||
$ingredientDeserializerFunc = \Closure::fromCallable([self::class, "deserializeIngredient"]);
|
||||
$itemDeserializerFunc = \Closure::fromCallable([self::class, 'deserializeItemStack']);
|
||||
|
||||
foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'shapeless_crafting.json'), ShapelessRecipeData::class) as $recipe){
|
||||
$recipeType = match($recipe->block){
|
||||
"crafting_table" => ShapelessRecipeType::CRAFTING(),
|
||||
@ -225,7 +222,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$inputs = [];
|
||||
foreach($recipe->input as $inputData){
|
||||
$input = $ingredientDeserializerFunc($inputData);
|
||||
$input = self::deserializeIngredient($inputData);
|
||||
if($input === null){ //unknown input item
|
||||
continue 2;
|
||||
}
|
||||
@ -233,7 +230,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$outputs = [];
|
||||
foreach($recipe->output as $outputData){
|
||||
$output = $itemDeserializerFunc($outputData);
|
||||
$output = self::deserializeItemStack($outputData);
|
||||
if($output === null){ //unknown output item
|
||||
continue 2;
|
||||
}
|
||||
@ -251,7 +248,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$inputs = [];
|
||||
foreach(Utils::stringifyKeys($recipe->input) as $symbol => $inputData){
|
||||
$input = $ingredientDeserializerFunc($inputData);
|
||||
$input = self::deserializeIngredient($inputData);
|
||||
if($input === null){ //unknown input item
|
||||
continue 2;
|
||||
}
|
||||
@ -259,7 +256,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$outputs = [];
|
||||
foreach($recipe->output as $outputData){
|
||||
$output = $itemDeserializerFunc($outputData);
|
||||
$output = self::deserializeItemStack($outputData);
|
||||
if($output === null){ //unknown output item
|
||||
continue 2;
|
||||
}
|
||||
|
@ -29,11 +29,13 @@ use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\thread\ThreadCrashInfoFrame;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function base64_encode;
|
||||
use function error_get_last;
|
||||
use function file;
|
||||
@ -186,7 +188,7 @@ class CrashDump{
|
||||
if($error === null){
|
||||
throw new \RuntimeException("Crash error information missing - did something use exit()?");
|
||||
}
|
||||
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["fullFile"] = $error["file"];
|
||||
$error["file"] = Filesystem::cleanPath($error["file"]);
|
||||
try{
|
||||
@ -201,9 +203,6 @@ class CrashDump{
|
||||
$error["message"] = mb_scrub($error["message"], 'UTF-8');
|
||||
|
||||
if(isset($lastError)){
|
||||
if(isset($lastError["trace"])){
|
||||
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
|
||||
}
|
||||
$this->data->lastError = $lastError;
|
||||
$this->data->lastError["message"] = mb_scrub($this->data->lastError["message"], 'UTF-8');
|
||||
}
|
||||
@ -215,10 +214,11 @@ class CrashDump{
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
|
||||
foreach($error["trace"] as $frame){
|
||||
if(!isset($frame["file"])){
|
||||
$frameFile = $frame->getFile();
|
||||
if($frameFile === null){
|
||||
continue; //PHP core
|
||||
}
|
||||
if($this->determinePluginFromFile($frame["file"], false)){
|
||||
if($this->determinePluginFromFile($frameFile, false)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -233,7 +233,8 @@ class CrashDump{
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->trace = Utils::printableTrace($error["trace"]);
|
||||
$this->data->trace = array_map(array: $error["trace"], callback: fn(ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
|
||||
$this->data->thread = $error["thread"];
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
|
@ -37,6 +37,8 @@ final class CrashDumpData implements \JsonSerializable{
|
||||
/** @var mixed[] */
|
||||
public array $error;
|
||||
|
||||
public string $thread;
|
||||
|
||||
public string $plugin_involvement;
|
||||
|
||||
public string $plugin = "";
|
||||
|
@ -64,6 +64,7 @@ final class CrashDumpRenderer{
|
||||
|
||||
$this->addLine();
|
||||
|
||||
$this->addLine("Thread: " . $this->data->thread);
|
||||
$this->addLine("Error: " . $this->data->error["message"]);
|
||||
$this->addLine("File: " . $this->data->error["file"]);
|
||||
$this->addLine("Line: " . $this->data->error["line"]);
|
||||
|
@ -62,6 +62,7 @@ final class EnchantmentIdMap{
|
||||
$this->register(EnchantmentIds::FIRE_ASPECT, VanillaEnchantments::FIRE_ASPECT());
|
||||
|
||||
$this->register(EnchantmentIds::EFFICIENCY, VanillaEnchantments::EFFICIENCY());
|
||||
$this->register(EnchantmentIds::FORTUNE, VanillaEnchantments::FORTUNE());
|
||||
$this->register(EnchantmentIds::SILK_TOUCH, VanillaEnchantments::SILK_TOUCH());
|
||||
$this->register(EnchantmentIds::UNBREAKING, VanillaEnchantments::UNBREAKING());
|
||||
|
||||
|
@ -84,6 +84,7 @@ final class PotionTypeIdMap{
|
||||
$this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER());
|
||||
$this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING());
|
||||
$this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING());
|
||||
$this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS());
|
||||
}
|
||||
|
||||
private function register(int $id, PotionType $type) : void{
|
||||
|
@ -66,4 +66,5 @@ final class PotionTypeIds{
|
||||
public const STRONG_TURTLE_MASTER = 39;
|
||||
public const SLOW_FALLING = 40;
|
||||
public const LONG_SLOW_FALLING = 41;
|
||||
public const STRONG_SLOWNESS = 42;
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ use pocketmine\block\Barrel;
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\Beetroot;
|
||||
use pocketmine\block\Bell;
|
||||
use pocketmine\block\BigDripleafHead;
|
||||
use pocketmine\block\BigDripleafStem;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BoneBlock;
|
||||
use pocketmine\block\BrewingStand;
|
||||
@ -115,6 +117,7 @@ use pocketmine\block\SeaPickle;
|
||||
use pocketmine\block\SimplePillar;
|
||||
use pocketmine\block\SimplePressurePlate;
|
||||
use pocketmine\block\Slab;
|
||||
use pocketmine\block\SmallDripleaf;
|
||||
use pocketmine\block\SnowLayer;
|
||||
use pocketmine\block\Sponge;
|
||||
use pocketmine\block\StainedGlass;
|
||||
@ -138,6 +141,7 @@ use pocketmine\block\UnderwaterTorch;
|
||||
use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DripleafState;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
@ -958,6 +962,24 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
|
||||
});
|
||||
$this->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{
|
||||
return Writer::create(Ids::BIG_DRIPLEAF)
|
||||
->writeLegacyHorizontalFacing($block->getFacing())
|
||||
->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()->id()){
|
||||
DripleafState::STABLE()->id() => StringValues::BIG_DRIPLEAF_TILT_NONE,
|
||||
DripleafState::UNSTABLE()->id() => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE,
|
||||
DripleafState::PARTIAL_TILT()->id() => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT,
|
||||
DripleafState::FULL_TILT()->id() => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT,
|
||||
default => throw new BlockStateSerializeException("Invalid Dripleaf tilt type " . $block->getLeafState()->name())
|
||||
})
|
||||
->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true);
|
||||
});
|
||||
$this->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{
|
||||
return Writer::create(Ids::BIG_DRIPLEAF)
|
||||
->writeLegacyHorizontalFacing($block->getFacing())
|
||||
->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE)
|
||||
->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false);
|
||||
});
|
||||
$this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH));
|
||||
$this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
|
||||
@ -1454,6 +1476,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeBool(StateNames::DEAD_BIT, !$block->isUnderwater())
|
||||
->writeInt(StateNames::CLUSTER_COUNT, $block->getCount() - 1);
|
||||
});
|
||||
$this->map(Blocks::SMALL_DRIPLEAF(), function(SmallDripleaf $block) : Writer{
|
||||
return Writer::create(Ids::SMALL_DRIPLEAF_BLOCK)
|
||||
->writeLegacyHorizontalFacing($block->getFacing())
|
||||
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
|
||||
});
|
||||
$this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER));
|
||||
$this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(StringValues::CHISEL_TYPE_SMOOTH, Axis::Y));
|
||||
$this->map(Blocks::SMOOTH_QUARTZ_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ));
|
||||
|
@ -236,9 +236,12 @@ final class BlockStateDeserializerHelper{
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public static function decodeStem(Stem $block, BlockStateReader $in) : Stem{
|
||||
//TODO: our stems don't support facings yet (facing_direction)
|
||||
$in->todo(BlockStateNames::FACING_DIRECTION);
|
||||
return self::decodeCrops($block, $in);
|
||||
//In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the
|
||||
//most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which
|
||||
//is absurd, and I refuse to make our API similarly absurd.
|
||||
$facing = $in->readFacingWithoutUp();
|
||||
return self::decodeCrops($block, $in)
|
||||
->setFacing($facing === Facing::DOWN ? Facing::UP : $facing);
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
|
@ -223,8 +223,12 @@ final class BlockStateSerializerHelper{
|
||||
}
|
||||
|
||||
public static function encodeStem(Stem $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
//In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the
|
||||
//most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which
|
||||
//is absurd, and I refuse to make our API similarly absurd.
|
||||
$facing = $block->getFacing();
|
||||
return self::encodeCrops($block, $out)
|
||||
->writeHorizontalFacing(Facing::NORTH); //TODO: PM impl doesn't support this yet
|
||||
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
|
||||
}
|
||||
|
||||
public static function encodeStone(string $type) : BlockStateWriter{
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DripleafState;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
@ -826,6 +827,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
->setAttachmentType($in->readBellAttachmentType());
|
||||
});
|
||||
$this->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{
|
||||
if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){
|
||||
return Blocks::BIG_DRIPLEAF_HEAD()
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
->setLeafState(match($type = $in->readString(StateNames::BIG_DRIPLEAF_TILT)){
|
||||
StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE(),
|
||||
StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE(),
|
||||
StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT => DripleafState::PARTIAL_TILT(),
|
||||
StringValues::BIG_DRIPLEAF_TILT_FULL_TILT => DripleafState::FULL_TILT(),
|
||||
default => throw $in->badValueException(StateNames::BIG_DRIPLEAF_TILT, $type),
|
||||
});
|
||||
}else{
|
||||
$in->ignored(StateNames::BIG_DRIPLEAF_TILT);
|
||||
return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readLegacyHorizontalFacing());
|
||||
}
|
||||
});
|
||||
$this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB());
|
||||
$this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS());
|
||||
$this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in));
|
||||
@ -1332,6 +1349,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readHorizontalFacing())
|
||||
->setLit(false);
|
||||
});
|
||||
$this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{
|
||||
return Blocks::SMALL_DRIPLEAF()
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
|
||||
});
|
||||
$this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS());
|
||||
$this->mapStairs(Ids::SMOOTH_RED_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_RED_SANDSTONE_STAIRS());
|
||||
$this->mapStairs(Ids::SMOOTH_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_SANDSTONE_STAIRS());
|
||||
|
@ -37,6 +37,8 @@ interface RuntimeEnumDescriber{
|
||||
|
||||
public function dirtType(\pocketmine\block\utils\DirtType &$value) : void;
|
||||
|
||||
public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void;
|
||||
|
||||
public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void;
|
||||
|
||||
public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void;
|
||||
|
@ -71,6 +71,16 @@ trait RuntimeEnumDeserializerTrait{
|
||||
};
|
||||
}
|
||||
|
||||
public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
|
||||
$value = match($this->readInt(2)){
|
||||
0 => \pocketmine\block\utils\DripleafState::FULL_TILT(),
|
||||
1 => \pocketmine\block\utils\DripleafState::PARTIAL_TILT(),
|
||||
2 => \pocketmine\block\utils\DripleafState::STABLE(),
|
||||
3 => \pocketmine\block\utils\DripleafState::UNSTABLE(),
|
||||
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DripleafState")
|
||||
};
|
||||
}
|
||||
|
||||
public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
|
||||
$value = match($this->readInt(4)){
|
||||
0 => \pocketmine\block\utils\DyeColor::BLACK(),
|
||||
@ -190,16 +200,17 @@ trait RuntimeEnumDeserializerTrait{
|
||||
29 => \pocketmine\item\PotionType::STRONG_LEAPING(),
|
||||
30 => \pocketmine\item\PotionType::STRONG_POISON(),
|
||||
31 => \pocketmine\item\PotionType::STRONG_REGENERATION(),
|
||||
32 => \pocketmine\item\PotionType::STRONG_STRENGTH(),
|
||||
33 => \pocketmine\item\PotionType::STRONG_SWIFTNESS(),
|
||||
34 => \pocketmine\item\PotionType::STRONG_TURTLE_MASTER(),
|
||||
35 => \pocketmine\item\PotionType::SWIFTNESS(),
|
||||
36 => \pocketmine\item\PotionType::THICK(),
|
||||
37 => \pocketmine\item\PotionType::TURTLE_MASTER(),
|
||||
38 => \pocketmine\item\PotionType::WATER(),
|
||||
39 => \pocketmine\item\PotionType::WATER_BREATHING(),
|
||||
40 => \pocketmine\item\PotionType::WEAKNESS(),
|
||||
41 => \pocketmine\item\PotionType::WITHER(),
|
||||
32 => \pocketmine\item\PotionType::STRONG_SLOWNESS(),
|
||||
33 => \pocketmine\item\PotionType::STRONG_STRENGTH(),
|
||||
34 => \pocketmine\item\PotionType::STRONG_SWIFTNESS(),
|
||||
35 => \pocketmine\item\PotionType::STRONG_TURTLE_MASTER(),
|
||||
36 => \pocketmine\item\PotionType::SWIFTNESS(),
|
||||
37 => \pocketmine\item\PotionType::THICK(),
|
||||
38 => \pocketmine\item\PotionType::TURTLE_MASTER(),
|
||||
39 => \pocketmine\item\PotionType::WATER(),
|
||||
40 => \pocketmine\item\PotionType::WATER_BREATHING(),
|
||||
41 => \pocketmine\item\PotionType::WEAKNESS(),
|
||||
42 => \pocketmine\item\PotionType::WITHER(),
|
||||
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for PotionType")
|
||||
};
|
||||
}
|
||||
|
@ -71,6 +71,16 @@ trait RuntimeEnumSerializerTrait{
|
||||
});
|
||||
}
|
||||
|
||||
public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
|
||||
$this->writeInt(2, match($value){
|
||||
\pocketmine\block\utils\DripleafState::FULL_TILT() => 0,
|
||||
\pocketmine\block\utils\DripleafState::PARTIAL_TILT() => 1,
|
||||
\pocketmine\block\utils\DripleafState::STABLE() => 2,
|
||||
\pocketmine\block\utils\DripleafState::UNSTABLE() => 3,
|
||||
default => throw new \pocketmine\utils\AssumptionFailedError("All DripleafState cases should be covered")
|
||||
});
|
||||
}
|
||||
|
||||
public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
|
||||
$this->writeInt(4, match($value){
|
||||
\pocketmine\block\utils\DyeColor::BLACK() => 0,
|
||||
@ -190,16 +200,17 @@ trait RuntimeEnumSerializerTrait{
|
||||
\pocketmine\item\PotionType::STRONG_LEAPING() => 29,
|
||||
\pocketmine\item\PotionType::STRONG_POISON() => 30,
|
||||
\pocketmine\item\PotionType::STRONG_REGENERATION() => 31,
|
||||
\pocketmine\item\PotionType::STRONG_STRENGTH() => 32,
|
||||
\pocketmine\item\PotionType::STRONG_SWIFTNESS() => 33,
|
||||
\pocketmine\item\PotionType::STRONG_TURTLE_MASTER() => 34,
|
||||
\pocketmine\item\PotionType::SWIFTNESS() => 35,
|
||||
\pocketmine\item\PotionType::THICK() => 36,
|
||||
\pocketmine\item\PotionType::TURTLE_MASTER() => 37,
|
||||
\pocketmine\item\PotionType::WATER() => 38,
|
||||
\pocketmine\item\PotionType::WATER_BREATHING() => 39,
|
||||
\pocketmine\item\PotionType::WEAKNESS() => 40,
|
||||
\pocketmine\item\PotionType::WITHER() => 41,
|
||||
\pocketmine\item\PotionType::STRONG_SLOWNESS() => 32,
|
||||
\pocketmine\item\PotionType::STRONG_STRENGTH() => 33,
|
||||
\pocketmine\item\PotionType::STRONG_SWIFTNESS() => 34,
|
||||
\pocketmine\item\PotionType::STRONG_TURTLE_MASTER() => 35,
|
||||
\pocketmine\item\PotionType::SWIFTNESS() => 36,
|
||||
\pocketmine\item\PotionType::THICK() => 37,
|
||||
\pocketmine\item\PotionType::TURTLE_MASTER() => 38,
|
||||
\pocketmine\item\PotionType::WATER() => 39,
|
||||
\pocketmine\item\PotionType::WATER_BREATHING() => 40,
|
||||
\pocketmine\item\PotionType::WEAKNESS() => 41,
|
||||
\pocketmine\item\PotionType::WITHER() => 42,
|
||||
default => throw new \pocketmine\utils\AssumptionFailedError("All PotionType cases should be covered")
|
||||
});
|
||||
}
|
||||
|
@ -47,6 +47,10 @@ trait RuntimeEnumSizeCalculatorTrait{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
||||
public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
||||
public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
|
||||
$this->addBits(4);
|
||||
}
|
||||
|
@ -80,6 +80,17 @@ use const M_PI;
|
||||
abstract class Living extends Entity{
|
||||
protected const DEFAULT_BREATH_TICKS = 300;
|
||||
|
||||
/**
|
||||
* The default knockback multiplier when an entity is hit by another entity.
|
||||
* Larger values knock the entity back with increased velocity.
|
||||
*/
|
||||
public const DEFAULT_KNOCKBACK_FORCE = 0.4;
|
||||
/**
|
||||
* Limit of an entity's vertical knockback velocity when hit by another entity. Without this limit, the entity
|
||||
* may be knocked far up into the air with large knockback forces.
|
||||
*/
|
||||
public const DEFAULT_KNOCKBACK_VERTICAL_LIMIT = 0.4;
|
||||
|
||||
private const TAG_LEGACY_HEALTH = "HealF"; //TAG_Float
|
||||
private const TAG_HEALTH = "Health"; //TAG_Float
|
||||
private const TAG_BREATH_TICKS = "Air"; //TAG_Short
|
||||
@ -217,6 +228,7 @@ abstract class Living extends Entity{
|
||||
public function setSneaking(bool $value = true) : void{
|
||||
$this->sneaking = $value;
|
||||
$this->networkPropertiesDirty = true;
|
||||
$this->recalculateSize();
|
||||
}
|
||||
|
||||
public function isSprinting() : bool{
|
||||
@ -258,6 +270,8 @@ abstract class Living extends Entity{
|
||||
if($this->isSwimming() || $this->isGliding()){
|
||||
$width = $size->getWidth();
|
||||
$this->setSize((new EntitySizeInfo($width, $width, $width * 0.9))->scale($this->getScale()));
|
||||
}elseif($this->isSneaking()){
|
||||
$this->setSize((new EntitySizeInfo(3 / 4 * $size->getHeight(), $size->getWidth(), 3 / 4 * $size->getEyeHeight()))->scale($this->getScale()));
|
||||
}else{
|
||||
$this->setSize($size->scale($this->getScale()));
|
||||
}
|
||||
@ -543,14 +557,14 @@ abstract class Living extends Entity{
|
||||
$e = $source->getChild();
|
||||
if($e !== null){
|
||||
$motion = $e->getMotion();
|
||||
$this->knockBack($motion->x, $motion->z, $source->getKnockBack());
|
||||
$this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
}
|
||||
}elseif($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
if($e !== null){
|
||||
$deltaX = $this->location->x - $e->location->x;
|
||||
$deltaZ = $this->location->z - $e->location->z;
|
||||
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack());
|
||||
$this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,7 +578,7 @@ abstract class Living extends Entity{
|
||||
$this->broadcastAnimation(new HurtAnimation($this));
|
||||
}
|
||||
|
||||
public function knockBack(float $x, float $z, float $force = 0.4, ?float $verticalLimit = 0.4) : void{
|
||||
public function knockBack(float $x, float $z, float $force = self::DEFAULT_KNOCKBACK_FORCE, ?float $verticalLimit = self::DEFAULT_KNOCKBACK_VERTICAL_LIMIT) : void{
|
||||
$f = sqrt($x * $x + $z * $z);
|
||||
if($f <= 0){
|
||||
return;
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\animation;
|
||||
|
||||
use pocketmine\entity\Human;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\ActorEventPacket;
|
||||
@ -32,7 +32,7 @@ use pocketmine\network\mcpe\protocol\types\ActorEvent;
|
||||
final class ConsumingItemAnimation implements Animation{
|
||||
|
||||
public function __construct(
|
||||
private Human $human, //TODO: maybe this can be expanded to more than just player entities?
|
||||
private Living $entity,
|
||||
private Item $item
|
||||
){}
|
||||
|
||||
@ -40,7 +40,7 @@ final class ConsumingItemAnimation implements Animation{
|
||||
[$netId, $netData] = TypeConverter::getInstance()->getItemTranslator()->toNetworkId($this->item);
|
||||
return [
|
||||
//TODO: need to check the data values
|
||||
ActorEventPacket::create($this->human->getId(), ActorEvent::EATING_ITEM, ($netId << 16) | $netData)
|
||||
ActorEventPacket::create($this->entity->getId(), ActorEvent::EATING_ITEM, ($netId << 16) | $netData)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,15 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
/**
|
||||
* @param float[] $modifiers
|
||||
*/
|
||||
public function __construct(Entity $damager, Entity $entity, int $cause, float $damage, array $modifiers = [], private float $knockBack = 0.4){
|
||||
public function __construct(
|
||||
Entity $damager,
|
||||
Entity $entity,
|
||||
int $cause,
|
||||
float $damage,
|
||||
array $modifiers = [],
|
||||
private float $knockBack = Living::DEFAULT_KNOCKBACK_FORCE,
|
||||
private float $verticalKnockBackLimit = Living::DEFAULT_KNOCKBACK_VERTICAL_LIMIT
|
||||
){
|
||||
$this->damagerEntityId = $damager->getId();
|
||||
parent::__construct($entity, $cause, $damage, $modifiers);
|
||||
$this->addAttackerModifiers($damager);
|
||||
@ -62,11 +70,39 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
return $this->getEntity()->getWorld()->getServer()->getWorldManager()->findEntity($this->damagerEntityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the force with which the victim will be knocked back from the attacking entity.
|
||||
*
|
||||
* @see Living::DEFAULT_KNOCKBACK_FORCE
|
||||
*/
|
||||
public function getKnockBack() : float{
|
||||
return $this->knockBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the force with which the victim will be knocked back from the attacking entity.
|
||||
* Larger values will knock the victim back further.
|
||||
* Negative values will pull the victim towards the attacker.
|
||||
*/
|
||||
public function setKnockBack(float $knockBack) : void{
|
||||
$this->knockBack = $knockBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum upwards velocity the victim may have after being knocked back.
|
||||
* This ensures that the victim doesn't fly up into the sky when high levels of knockback are applied.
|
||||
*
|
||||
* @see Living::DEFAULT_KNOCKBACK_VERTICAL_LIMIT
|
||||
*/
|
||||
public function getVerticalKnockBackLimit() : float{
|
||||
return $this->verticalKnockBackLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum upwards velocity the victim may have after being knocked back.
|
||||
* Larger values will allow the victim to fly higher if the knockback force is also large.
|
||||
*/
|
||||
public function setVerticalKnockBackLimit(float $verticalKnockBackLimit) : void{
|
||||
$this->verticalKnockBackLimit = $verticalKnockBackLimit;
|
||||
}
|
||||
}
|
||||
|
39
src/event/player/PlayerMissedSwingEvent.php
Normal file
39
src/event/player/PlayerMissedSwingEvent.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Called when a player attempts to perform the attack action (left-click) without a target entity.
|
||||
*/
|
||||
class PlayerMissedSwingEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(Player $player){
|
||||
$this->player = $player;
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory\transaction\action;
|
||||
|
||||
use pocketmine\inventory\CreativeInventory;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
@ -43,7 +42,7 @@ class CreateItemAction extends InventoryAction{
|
||||
if($source->hasFiniteResources()){
|
||||
throw new TransactionValidationException("Player has finite resources, cannot create items");
|
||||
}
|
||||
if(!CreativeInventory::getInstance()->contains($this->sourceItem)){
|
||||
if(!$source->getCreativeInventory()->contains($this->sourceItem)){
|
||||
throw new TransactionValidationException("Creative inventory does not contain requested item");
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ use pocketmine\utils\EnumTrait;
|
||||
* @method static PotionType STRONG_LEAPING()
|
||||
* @method static PotionType STRONG_POISON()
|
||||
* @method static PotionType STRONG_REGENERATION()
|
||||
* @method static PotionType STRONG_SLOWNESS()
|
||||
* @method static PotionType STRONG_STRENGTH()
|
||||
* @method static PotionType STRONG_SWIFTNESS()
|
||||
* @method static PotionType STRONG_TURTLE_MASTER()
|
||||
@ -201,6 +202,9 @@ final class PotionType{
|
||||
]),
|
||||
new self("long_slow_falling", "Long Slow Falling", fn() => [
|
||||
//TODO
|
||||
]),
|
||||
new self("strong_slowness", "Strong Slowness", fn() => [
|
||||
new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 3)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
@ -155,6 +155,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("beetroot_block", fn() => Blocks::BEETROOTS());
|
||||
$result->registerBlock("beetroots", fn() => Blocks::BEETROOTS());
|
||||
$result->registerBlock("bell", fn() => Blocks::BELL());
|
||||
$result->registerBlock("big_dripleaf", fn() => Blocks::BIG_DRIPLEAF_HEAD());
|
||||
$result->registerBlock("birch_button", fn() => Blocks::BIRCH_BUTTON());
|
||||
$result->registerBlock("birch_door", fn() => Blocks::BIRCH_DOOR());
|
||||
$result->registerBlock("birch_door_block", fn() => Blocks::BIRCH_DOOR());
|
||||
@ -972,6 +973,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("slabs", fn() => Blocks::SMOOTH_STONE_SLAB());
|
||||
$result->registerBlock("slime", fn() => Blocks::SLIME());
|
||||
$result->registerBlock("slime_block", fn() => Blocks::SLIME());
|
||||
$result->registerBlock("small_dripleaf", fn() => Blocks::SMALL_DRIPLEAF());
|
||||
$result->registerBlock("smoker", fn() => Blocks::SMOKER());
|
||||
$result->registerBlock("smooth_basalt", fn() => Blocks::SMOOTH_BASALT());
|
||||
$result->registerBlock("smooth_quartz", fn() => Blocks::SMOOTH_QUARTZ());
|
||||
@ -1506,6 +1508,8 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("strong_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_POISON()));
|
||||
$result->register("strong_regeneration_potion", fn() => Items::POTION()->setType(PotionType::STRONG_REGENERATION()));
|
||||
$result->register("strong_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_REGENERATION()));
|
||||
$result->register("strong_slowness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SLOWNESS()));
|
||||
$result->register("strong_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SLOWNESS()));
|
||||
$result->register("strong_strength_potion", fn() => Items::POTION()->setType(PotionType::STRONG_STRENGTH()));
|
||||
$result->register("strong_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_STRENGTH()));
|
||||
$result->register("strong_swiftness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SWIFTNESS()));
|
||||
|
@ -43,6 +43,7 @@ final class StringToEnchantmentParser extends StringToTParser{
|
||||
$result->register("fire_aspect", fn() => VanillaEnchantments::FIRE_ASPECT());
|
||||
$result->register("fire_protection", fn() => VanillaEnchantments::FIRE_PROTECTION());
|
||||
$result->register("flame", fn() => VanillaEnchantments::FLAME());
|
||||
$result->register("fortune", fn() => VanillaEnchantments::FORTUNE());
|
||||
$result->register("infinity", fn() => VanillaEnchantments::INFINITY());
|
||||
$result->register("knockback", fn() => VanillaEnchantments::KNOCKBACK());
|
||||
$result->register("mending", fn() => VanillaEnchantments::MENDING());
|
||||
|
@ -39,6 +39,7 @@ use pocketmine\utils\RegistryTrait;
|
||||
* @method static FireAspectEnchantment FIRE_ASPECT()
|
||||
* @method static ProtectionEnchantment FIRE_PROTECTION()
|
||||
* @method static Enchantment FLAME()
|
||||
* @method static Enchantment FORTUNE()
|
||||
* @method static Enchantment INFINITY()
|
||||
* @method static KnockbackEnchantment KNOCKBACK()
|
||||
* @method static Enchantment MENDING()
|
||||
@ -85,6 +86,7 @@ final class VanillaEnchantments{
|
||||
self::register("FIRE_ASPECT", new FireAspectEnchantment(KnownTranslationFactory::enchantment_fire(), Rarity::RARE, ItemFlags::SWORD, ItemFlags::NONE, 2));
|
||||
|
||||
self::register("EFFICIENCY", new Enchantment(KnownTranslationFactory::enchantment_digging(), Rarity::COMMON, ItemFlags::DIG, ItemFlags::SHEARS, 5));
|
||||
self::register("FORTUNE", new Enchantment(KnownTranslationFactory::enchantment_lootBonusDigger(), Rarity::RARE, ItemFlags::DIG, ItemFlags::NONE, 3));
|
||||
self::register("SILK_TOUCH", new Enchantment(KnownTranslationFactory::enchantment_untouching(), Rarity::MYTHIC, ItemFlags::DIG, ItemFlags::SHEARS, 1));
|
||||
self::register("UNBREAKING", new Enchantment(KnownTranslationFactory::enchantment_durability(), Rarity::UNCOMMON, ItemFlags::DIG | ItemFlags::ARMOR | ItemFlags::FISHING_ROD | ItemFlags::BOW, ItemFlags::TOOL | ItemFlags::CARROT_STICK | ItemFlags::ELYTRA, 3));
|
||||
|
||||
|
@ -35,7 +35,6 @@ use pocketmine\block\inventory\LoomInventory;
|
||||
use pocketmine\block\inventory\SmithingTableInventory;
|
||||
use pocketmine\block\inventory\StonecutterInventory;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\inventory\CreativeInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
@ -109,7 +108,7 @@ class InventoryManager{
|
||||
private NetworkSession $session
|
||||
){
|
||||
$this->containerOpenCallbacks = new ObjectSet();
|
||||
$this->containerOpenCallbacks->add(\Closure::fromCallable([self::class, 'createContainerOpen']));
|
||||
$this->containerOpenCallbacks->add(self::createContainerOpen(...));
|
||||
|
||||
$this->add(ContainerIds::INVENTORY, $this->player->getInventory());
|
||||
$this->add(ContainerIds::OFFHAND, $this->player->getOffHandInventory());
|
||||
@ -117,9 +116,7 @@ class InventoryManager{
|
||||
$this->addComplex(UIInventorySlotOffset::CURSOR, $this->player->getCursorInventory());
|
||||
$this->addComplex(UIInventorySlotOffset::CRAFTING2X2_INPUT, $this->player->getCraftingGrid());
|
||||
|
||||
$this->player->getInventory()->getHeldItemIndexChangeListeners()->add(function() : void{
|
||||
$this->syncSelectedHotbarSlot();
|
||||
});
|
||||
$this->player->getInventory()->getHeldItemIndexChangeListeners()->add($this->syncSelectedHotbarSlot(...));
|
||||
}
|
||||
|
||||
private function associateIdWithInventory(int $id, Inventory $inventory) : void{
|
||||
@ -603,7 +600,7 @@ class InventoryManager{
|
||||
}
|
||||
|
||||
public function syncCreative() : void{
|
||||
$this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache(CreativeInventory::getInstance()));
|
||||
$this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache($this->player->getCreativeInventory()));
|
||||
}
|
||||
|
||||
private function newItemStackId() : int{
|
||||
|
@ -23,28 +23,26 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use FG\ASN1\Exception\ParserException;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\Utils;
|
||||
use function base64_decode;
|
||||
use function base64_encode;
|
||||
use function bin2hex;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function gmp_export;
|
||||
use function gmp_import;
|
||||
use function gmp_init;
|
||||
use function gmp_strval;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function ltrim;
|
||||
use function openssl_error_string;
|
||||
use function openssl_pkey_get_details;
|
||||
use function openssl_pkey_get_public;
|
||||
use function openssl_sign;
|
||||
use function openssl_verify;
|
||||
use function ord;
|
||||
use function preg_match;
|
||||
use function rtrim;
|
||||
use function sprintf;
|
||||
@ -54,8 +52,7 @@ use function str_replace;
|
||||
use function str_split;
|
||||
use function strlen;
|
||||
use function strtr;
|
||||
use const GMP_BIG_ENDIAN;
|
||||
use const GMP_MSW_FIRST;
|
||||
use function substr;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const OPENSSL_ALGO_SHA384;
|
||||
use const STR_PAD_LEFT;
|
||||
@ -63,6 +60,12 @@ use const STR_PAD_LEFT;
|
||||
final class JwtUtils{
|
||||
public const BEDROCK_SIGNING_KEY_CURVE_NAME = "secp384r1";
|
||||
|
||||
private const ASN1_INTEGER_TAG = "\x02";
|
||||
private const ASN1_SEQUENCE_TAG = "\x30";
|
||||
|
||||
private const SIGNATURE_PART_LENGTH = 48;
|
||||
private const SIGNATURE_ALGORITHM = OPENSSL_ALGO_SHA384;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return array{string, string, string}
|
||||
@ -98,30 +101,84 @@ final class JwtUtils{
|
||||
return [$header, $body, $signature];
|
||||
}
|
||||
|
||||
private static function signaturePartToAsn1(string $part) : string{
|
||||
if(strlen($part) !== self::SIGNATURE_PART_LENGTH){
|
||||
throw new JwtException("R and S for a SHA384 signature must each be exactly 48 bytes, but have " . strlen($part) . " bytes");
|
||||
}
|
||||
$part = ltrim($part, "\x00");
|
||||
if(ord($part[0]) >= 128){
|
||||
//ASN.1 integers with a leading 1 bit are considered negative - add a leading 0 byte to prevent this
|
||||
//ECDSA signature R and S values are always positive
|
||||
$part = "\x00" . $part;
|
||||
}
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
return self::ASN1_INTEGER_TAG . chr(strlen($part)) . $part;
|
||||
}
|
||||
|
||||
private static function rawSignatureToDer(string $rawSignature) : string{
|
||||
if(strlen($rawSignature) !== self::SIGNATURE_PART_LENGTH * 2){
|
||||
throw new JwtException("JWT signature has unexpected length, expected 96, got " . strlen($rawSignature));
|
||||
}
|
||||
|
||||
[$rString, $sString] = str_split($rawSignature, self::SIGNATURE_PART_LENGTH);
|
||||
$sequence = self::signaturePartToAsn1($rString) . self::signaturePartToAsn1($sString);
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
return self::ASN1_SEQUENCE_TAG . chr(strlen($sequence)) . $sequence;
|
||||
}
|
||||
|
||||
private static function signaturePartFromAsn1(BinaryStream $stream) : string{
|
||||
$prefix = $stream->get(1);
|
||||
if($prefix !== self::ASN1_INTEGER_TAG){
|
||||
throw new \InvalidArgumentException("Expected an ASN.1 INTEGER tag, got " . bin2hex($prefix));
|
||||
}
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
$length = $stream->getByte();
|
||||
if($length > self::SIGNATURE_PART_LENGTH + 1){ //each part may have an extra leading 0 byte to prevent it being interpreted as a negative number
|
||||
throw new \InvalidArgumentException("Expected at most 49 bytes for signature R or S, got $length");
|
||||
}
|
||||
$part = $stream->get($length);
|
||||
return str_pad(ltrim($part, "\x00"), self::SIGNATURE_PART_LENGTH, "\x00", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private static function rawSignatureFromDer(string $derSignature) : string{
|
||||
if($derSignature[0] !== self::ASN1_SEQUENCE_TAG){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, expected ASN.1 SEQUENCE tag, got " . bin2hex($derSignature[0]));
|
||||
}
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
$length = ord($derSignature[1]);
|
||||
$parts = substr($derSignature, 2, $length);
|
||||
if(strlen($parts) !== $length){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, expected $length sequence bytes, got " . strlen($parts));
|
||||
}
|
||||
|
||||
$stream = new BinaryStream($parts);
|
||||
$rRaw = self::signaturePartFromAsn1($stream);
|
||||
$sRaw = self::signaturePartFromAsn1($stream);
|
||||
|
||||
if(!$stream->feof()){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, unexpected trailing sequence data");
|
||||
}
|
||||
|
||||
return $rRaw . $sRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws JwtException
|
||||
*/
|
||||
public static function verify(string $jwt, \OpenSSLAsymmetricKey $signingKey) : bool{
|
||||
[$header, $body, $signature] = self::split($jwt);
|
||||
|
||||
$plainSignature = self::b64UrlDecode($signature);
|
||||
if(strlen($plainSignature) !== 96){
|
||||
throw new JwtException("JWT signature has unexpected length, expected 96, got " . strlen($plainSignature));
|
||||
}
|
||||
|
||||
[$rString, $sString] = str_split($plainSignature, 48);
|
||||
$convert = fn(string $str) => gmp_strval(gmp_import($str, 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST), 10);
|
||||
|
||||
$sequence = new Sequence(
|
||||
new Integer($convert($rString)),
|
||||
new Integer($convert($sString))
|
||||
);
|
||||
$rawSignature = self::b64UrlDecode($signature);
|
||||
$derSignature = self::rawSignatureToDer($rawSignature);
|
||||
|
||||
$v = openssl_verify(
|
||||
$header . '.' . $body,
|
||||
$sequence->getBinary(),
|
||||
$derSignature,
|
||||
$signingKey,
|
||||
OPENSSL_ALGO_SHA384
|
||||
self::SIGNATURE_ALGORITHM
|
||||
);
|
||||
switch($v){
|
||||
case 0: return false;
|
||||
@ -140,33 +197,13 @@ final class JwtUtils{
|
||||
|
||||
openssl_sign(
|
||||
$jwtBody,
|
||||
$rawDerSig,
|
||||
$derSignature,
|
||||
$signingKey,
|
||||
OPENSSL_ALGO_SHA384
|
||||
self::SIGNATURE_ALGORITHM
|
||||
);
|
||||
|
||||
try{
|
||||
$asnObject = Sequence::fromBinary($rawDerSig);
|
||||
}catch(ParserException $e){
|
||||
throw new AssumptionFailedError("Failed to parse OpenSSL signature: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
if(count($asnObject) !== 2){
|
||||
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected exactly 2 parts");
|
||||
}
|
||||
[$r, $s] = [$asnObject[0], $asnObject[1]];
|
||||
if(!($r instanceof Integer) || !($s instanceof Integer)){
|
||||
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected 2 INTEGER parts");
|
||||
}
|
||||
$rString = $r->getContent();
|
||||
$sString = $s->getContent();
|
||||
|
||||
$toBinary = fn($str) => str_pad(
|
||||
gmp_export(gmp_init($str, 10), 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST),
|
||||
48,
|
||||
"\x00",
|
||||
STR_PAD_LEFT
|
||||
);
|
||||
$jwtSig = JwtUtils::b64UrlEncode($toBinary($rString) . $toBinary($sString));
|
||||
$rawSignature = self::rawSignatureFromDer($derSignature);
|
||||
$jwtSig = self::b64UrlEncode($rawSignature);
|
||||
|
||||
return "$jwtBody.$jwtSig";
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ class NetworkSession{
|
||||
|
||||
$this->setHandler(new SessionStartPacketHandler(
|
||||
$this,
|
||||
fn() => $this->onSessionStartSuccess()
|
||||
$this->onSessionStartSuccess(...)
|
||||
));
|
||||
|
||||
$this->manager->add($this);
|
||||
@ -225,13 +225,13 @@ class NetworkSession{
|
||||
$this->logger->setPrefix($this->getLogPrefix());
|
||||
$this->manager->markLoginReceived($this);
|
||||
},
|
||||
\Closure::fromCallable([$this, "setAuthenticationStatus"])
|
||||
$this->setAuthenticationStatus(...)
|
||||
));
|
||||
}
|
||||
|
||||
protected function createPlayer() : void{
|
||||
$this->server->createPlayer($this, $this->info, $this->authenticated, $this->cachedOfflinePlayerData)->onCompletion(
|
||||
\Closure::fromCallable([$this, 'onPlayerCreated']),
|
||||
$this->onPlayerCreated(...),
|
||||
function() : void{
|
||||
//TODO: this should never actually occur... right?
|
||||
$this->logger->error("Failed to create player");
|
||||
@ -787,9 +787,7 @@ class NetworkSession{
|
||||
|
||||
$this->cipher = EncryptionContext::fakeGCM($encryptionKey);
|
||||
|
||||
$this->setHandler(new HandshakePacketHandler(function() : void{
|
||||
$this->onServerLoginSuccess();
|
||||
}));
|
||||
$this->setHandler(new HandshakePacketHandler($this->onServerLoginSuccess(...)));
|
||||
$this->logger->debug("Enabled encryption");
|
||||
}));
|
||||
}else{
|
||||
@ -818,9 +816,7 @@ class NetworkSession{
|
||||
public function notifyTerrainReady() : void{
|
||||
$this->logger->debug("Sending spawn notification, waiting for spawn response");
|
||||
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::PLAYER_SPAWN));
|
||||
$this->setHandler(new SpawnResponsePacketHandler(function() : void{
|
||||
$this->onClientSpawnResponse();
|
||||
}));
|
||||
$this->setHandler(new SpawnResponsePacketHandler($this->onClientSpawnResponse(...)));
|
||||
}
|
||||
|
||||
private function onClientSpawnResponse() : void{
|
||||
|
16
src/network/mcpe/cache/CraftingDataCache.php
vendored
16
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -25,21 +25,17 @@ namespace pocketmine\network\mcpe\cache;
|
||||
|
||||
use pocketmine\crafting\CraftingManager;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\crafting\RecipeIngredient;
|
||||
use pocketmine\crafting\ShapedRecipe;
|
||||
use pocketmine\crafting\ShapelessRecipe;
|
||||
use pocketmine\crafting\ShapelessRecipeType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\CraftingRecipeBlockName;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe as ProtocolFurnaceRecipe;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipeBlockName;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\PotionContainerChangeRecipe as ProtocolPotionContainerChangeRecipe;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\PotionTypeRecipe as ProtocolPotionTypeRecipe;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\ShapedRecipe as ProtocolShapedRecipe;
|
||||
use pocketmine\network\mcpe\protocol\types\recipe\ShapelessRecipe as ProtocolShapelessRecipe;
|
||||
use pocketmine\timings\Timings;
|
||||
@ -95,12 +91,8 @@ final class CraftingDataCache{
|
||||
$recipesWithTypeIds[] = new ProtocolShapelessRecipe(
|
||||
CraftingDataPacket::ENTRY_SHAPELESS,
|
||||
Binary::writeInt($index),
|
||||
array_map(function(RecipeIngredient $item) use ($converter) : ProtocolRecipeIngredient{
|
||||
return $converter->coreRecipeIngredientToNet($item);
|
||||
}, $recipe->getIngredientList()),
|
||||
array_map(function(Item $item) use ($converter) : ItemStack{
|
||||
return $converter->coreItemStackToNet($item);
|
||||
}, $recipe->getResults()),
|
||||
array_map($converter->coreRecipeIngredientToNet(...), $recipe->getIngredientList()),
|
||||
array_map($converter->coreItemStackToNet(...), $recipe->getResults()),
|
||||
$nullUUID,
|
||||
$typeTag,
|
||||
50,
|
||||
@ -118,9 +110,7 @@ final class CraftingDataCache{
|
||||
CraftingDataPacket::ENTRY_SHAPED,
|
||||
Binary::writeInt($index),
|
||||
$inputs,
|
||||
array_map(function(Item $item) use ($converter) : ItemStack{
|
||||
return $converter->coreItemStackToNet($item);
|
||||
}, $recipe->getResults()),
|
||||
array_map($converter->coreItemStackToNet(...), $recipe->getResults()),
|
||||
$nullUUID,
|
||||
CraftingRecipeBlockName::CRAFTING_TABLE,
|
||||
50,
|
||||
|
@ -236,6 +236,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){
|
||||
$this->player->jump();
|
||||
}
|
||||
if($packet->hasFlag(PlayerAuthInputFlags::MISSED_SWING)){
|
||||
$this->player->missSwing();
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->forceMoveSync && $hasMoved){
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user