Refactor Rail handling to allow LSP-complaint shape handling

the reason there hasn't been any API until now is because of how inconvenient it was to expose a LSP-compliant API _and_ use the same base class for handling all the connection logic. This commit fixes that problem by abstracting shape handling away from BaseRail entirely, so that now it deals exclusively with connections. Deciding the shape of rail to use is now the job of the subclasses.
This commit is contained in:
Dylan K. Taylor
2021-07-22 17:35:10 +01:00
parent 2476f40cc6
commit e97234d420
8 changed files with 210 additions and 138 deletions

View File

@ -23,74 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\block\utils\RailConnectionInfo;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function array_map;
use function array_reverse;
use function array_search;
use function array_shift;
use function count;
use function implode;
use function in_array;
abstract class BaseRail extends Flowable{
protected const FLAG_ASCEND = 1 << 24; //used to indicate direction-up
protected const CONNECTIONS = [
//straights
BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH => [
Facing::NORTH,
Facing::SOUTH
],
BlockLegacyMetadata::RAIL_STRAIGHT_EAST_WEST => [
Facing::EAST,
Facing::WEST
],
//ascending
BlockLegacyMetadata::RAIL_ASCENDING_EAST => [
Facing::WEST,
Facing::EAST | self::FLAG_ASCEND
],
BlockLegacyMetadata::RAIL_ASCENDING_WEST => [
Facing::EAST,
Facing::WEST | self::FLAG_ASCEND
],
BlockLegacyMetadata::RAIL_ASCENDING_NORTH => [
Facing::SOUTH,
Facing::NORTH | self::FLAG_ASCEND
],
BlockLegacyMetadata::RAIL_ASCENDING_SOUTH => [
Facing::NORTH,
Facing::SOUTH | self::FLAG_ASCEND
]
];
protected int $railShape = BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH;
protected function writeStateToMeta() : int{
return $this->railShape;
}
public function readStateFromData(int $id, int $stateMeta) : void{
$railShape = $this->readRailShapeFromMeta($stateMeta);
if($railShape === null){
throw new InvalidBlockStateException("Invalid rail type meta $stateMeta");
}
$this->railShape = $railShape;
}
abstract protected function readRailShapeFromMeta(int $stateMeta) : ?int;
public function getStateBitmask() : int{
return 0b1111;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
@ -108,28 +54,22 @@ abstract class BaseRail extends Flowable{
* @param int[][] $lookup
* @phpstan-param array<int, list<int>> $lookup
*/
protected static function searchState(array $connections, array $lookup) : int{
$meta = array_search($connections, $lookup, true);
if($meta === false){
$meta = array_search(array_reverse($connections), $lookup, true);
protected static function searchState(array $connections, array $lookup) : ?int{
$shape = array_search($connections, $lookup, true);
if($shape === false){
$shape = array_search(array_reverse($connections), $lookup, true);
}
if($meta === false){
throw new \InvalidArgumentException("No meta value matches connections " . implode(", ", array_map('\dechex', $connections)));
}
return $meta;
return $shape === false ? null : $shape;
}
/**
* Returns a meta value for the rail with the given connections.
* Sets the rail shape according to the given connections, if a shape matches.
*
* @param int[] $connections
*
* @throws \InvalidArgumentException if no state matches the given connections
* @throws \InvalidArgumentException if no shape matches the given connections
*/
protected function getShapeForConnections(array $connections) : int{
return self::searchState($connections, self::CONNECTIONS);
}
abstract protected function setShapeFromConnections(array $connections) : void;
/**
* Returns the connection directions of this rail (depending on the current block state)
@ -149,15 +89,15 @@ abstract class BaseRail extends Flowable{
/** @var int $connection */
foreach($this->getCurrentShapeConnections() as $connection){
$other = $this->getSide($connection & ~self::FLAG_ASCEND);
$otherConnection = Facing::opposite($connection & ~self::FLAG_ASCEND);
$other = $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND);
$otherConnection = Facing::opposite($connection & ~RailConnectionInfo::FLAG_ASCEND);
if(($connection & self::FLAG_ASCEND) !== 0){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0){
$other = $other->getSide(Facing::UP);
}elseif(!($other instanceof BaseRail)){ //check for rail sloping up to meet this one
$other = $other->getSide(Facing::DOWN);
$otherConnection |= self::FLAG_ASCEND;
$otherConnection |= RailConnectionInfo::FLAG_ASCEND;
}
if(
@ -188,7 +128,7 @@ abstract class BaseRail extends Flowable{
Facing::EAST => true
];
foreach($possible as $p => $_){
$possible[$p | self::FLAG_ASCEND] = true;
$possible[$p | RailConnectionInfo::FLAG_ASCEND] = true;
}
return $possible;
@ -206,13 +146,13 @@ abstract class BaseRail extends Flowable{
* @phpstan-return array<int, true>
*/
protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{
$opposite = Facing::opposite($constraint & ~self::FLAG_ASCEND);
$opposite = Facing::opposite($constraint & ~RailConnectionInfo::FLAG_ASCEND);
$possible = [$opposite => true];
if(($constraint & self::FLAG_ASCEND) === 0){
if(($constraint & RailConnectionInfo::FLAG_ASCEND) === 0){
//We can slope the other way if this connection isn't already a slope
$possible[$opposite | self::FLAG_ASCEND] = true;
$possible[$opposite | RailConnectionInfo::FLAG_ASCEND] = true;
}
return $possible;
@ -227,16 +167,16 @@ abstract class BaseRail extends Flowable{
$continue = false;
foreach($possible as $thisSide => $_){
$otherSide = Facing::opposite($thisSide & ~self::FLAG_ASCEND);
$otherSide = Facing::opposite($thisSide & ~RailConnectionInfo::FLAG_ASCEND);
$other = $this->getSide($thisSide & ~self::FLAG_ASCEND);
$other = $this->getSide($thisSide & ~RailConnectionInfo::FLAG_ASCEND);
if(($thisSide & self::FLAG_ASCEND) !== 0){
if(($thisSide & RailConnectionInfo::FLAG_ASCEND) !== 0){
$other = $other->getSide(Facing::UP);
}elseif(!($other instanceof BaseRail)){ //check if other rails can slope up to meet this one
$other = $other->getSide(Facing::DOWN);
$otherSide |= self::FLAG_ASCEND;
$otherSide |= RailConnectionInfo::FLAG_ASCEND;
}
if(!($other instanceof BaseRail) or count($otherConnections = $other->getConnectedDirections()) >= 2){
@ -271,12 +211,12 @@ abstract class BaseRail extends Flowable{
*/
private function setConnections(array $connections) : void{
if(count($connections) === 1){
$connections[] = Facing::opposite($connections[0] & ~self::FLAG_ASCEND);
$connections[] = Facing::opposite($connections[0] & ~RailConnectionInfo::FLAG_ASCEND);
}elseif(count($connections) !== 2){
throw new \InvalidArgumentException("Expected exactly 2 connections, got " . count($connections));
}
$this->railShape = $this->getShapeForConnections($connections);
$this->setShapeFromConnections($connections);
}
public function onNearbyBlockChange() : void{
@ -284,7 +224,7 @@ abstract class BaseRail extends Flowable{
$this->pos->getWorld()->useBreakOn($this->pos);
}else{
foreach($this->getCurrentShapeConnections() as $connection){
if(($connection & self::FLAG_ASCEND) !== 0 and $this->getSide($connection & ~self::FLAG_ASCEND)->isTransparent()){
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 and $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
$this->pos->getWorld()->useBreakOn($this->pos);
break;
}