A giant hack to cut down code needed for runtime block serialization by 50%

this also avoids repeated information and inconsistencies.
This commit is contained in:
Dylan K. Taylor
2022-07-18 18:25:41 +01:00
parent cf34f88a67
commit 6d4279671e
90 changed files with 380 additions and 717 deletions

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\runtime;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\RailConnectionInfo;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
@@ -50,20 +51,28 @@ final class RuntimeDataReader{
return $value;
}
public function readBoundedInt(int $bits, int $min, int $max) : int{
public function int(int $bits, int &$value) : void{
$value = $this->readInt($bits);
}
public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
$result = $this->readInt($bits) + $min;
if($result < $min || $result > $max){
throw new InvalidSerializedRuntimeDataException("Value is outside the range $min - $max");
}
return $result;
$value = $result;
}
public function readBool() : bool{
protected function readBool() : bool{
return $this->readInt(1) === 1;
}
public function readHorizontalFacing() : int{
return match($this->readInt(2)){
public function bool(bool &$value) : void{
$value = $this->readBool();
}
public function horizontalFacing(int &$facing) : void{
$facing = match($this->readInt(2)){
0 => Facing::NORTH,
1 => Facing::EAST,
2 => Facing::SOUTH,
@@ -73,9 +82,9 @@ final class RuntimeDataReader{
}
/**
* @return int[]
* @param int[] $faces
*/
public function readHorizontalFacingFlags() : array{
public function horizontalFacingFlags(array &$faces) : void{
$result = [];
foreach(Facing::HORIZONTAL as $facing){
if($this->readBool()){
@@ -83,11 +92,11 @@ final class RuntimeDataReader{
}
}
return $result;
$faces = $result;
}
public function readFacing() : int{
return match($this->readInt(3)){
public function facing(int &$facing) : void{
$facing = match($this->readInt(3)){
0 => Facing::DOWN,
1 => Facing::UP,
2 => Facing::NORTH,
@@ -98,8 +107,18 @@ final class RuntimeDataReader{
};
}
public function readAxis() : int{
return match($this->readInt(2)){
public function facingExcept(int &$facing, int $except) : void{
$result = 0;
$this->facing($result);
if($result === $except){
throw new InvalidSerializedRuntimeDataException("Illegal facing value");
}
$facing = $result;
}
public function axis(int &$axis) : void{
$axis = match($this->readInt(2)){
0 => Axis::X,
1 => Axis::Z,
2 => Axis::Y,
@@ -107,8 +126,8 @@ final class RuntimeDataReader{
};
}
public function readHorizontalAxis() : int{
return match($this->readInt(1)){
public function horizontalAxis(int &$axis) : void{
$axis = match($this->readInt(1)){
0 => Axis::X,
1 => Axis::Z,
default => throw new AssumptionFailedError("Unreachable")
@@ -116,16 +135,17 @@ final class RuntimeDataReader{
}
/**
* @return WallConnectionType[]
* @phpstan-return array<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType>
* @param WallConnectionType[] $connections
* @phpstan-param array<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType> $connections
*/
public function readWallConnections() : array{
$connections = [];
public function wallConnections(array &$connections) : void{
$result = [];
//TODO: we can pack this into 7 bits instead of 8
foreach(Facing::HORIZONTAL as $facing){
$type = $this->readBoundedInt(2, 0, 2);
$type = 0;
$this->boundedInt(2, 0, 2, $type);
if($type !== 0){
$connections[$facing] = match($type){
$result[$facing] = match($type){
1 => WallConnectionType::SHORT(),
2 => WallConnectionType::TALL(),
default => throw new AssumptionFailedError("Unreachable")
@@ -133,14 +153,14 @@ final class RuntimeDataReader{
}
}
return $connections;
$connections = $result;
}
/**
* @return BrewingStandSlot[]
* @phpstan-return array<int, BrewingStandSlot>
* @param BrewingStandSlot[] $slots
* @phpstan-param array<int, BrewingStandSlot> $slots
*/
public function readBrewingStandSlots() : array{
public function brewingStandSlots(array &$slots) : void{
$result = [];
foreach([
BrewingStandSlot::EAST(),
@@ -152,7 +172,25 @@ final class RuntimeDataReader{
}
}
return $result;
$slots = $result;
}
public function railShape(int &$railShape) : void{
$result = $this->readInt(4);
if(!isset(RailConnectionInfo::CONNECTIONS[$result]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$result])){
throw new InvalidSerializedRuntimeDataException("Invalid rail shape $result");
}
$railShape = $result;
}
public function straightOnlyRailShape(int &$railShape) : void{
$result = $this->readInt(3);
if(!isset(RailConnectionInfo::CONNECTIONS[$result])){
throw new InvalidSerializedRuntimeDataException("No rail shape matches meta $result");
}
$railShape = $result;
}
public function getOffset() : int{ return $this->offset; }

View File

@@ -40,8 +40,7 @@ final class RuntimeDataWriter{
private int $maxBits
){}
/** @return $this */
public function writeInt(int $bits, int $value) : self{
public function int(int $bits, int $value) : void{
if($this->offset + $bits > $this->maxBits){
throw new \InvalidArgumentException("Bit buffer cannot be larger than $this->maxBits bits (already have $this->offset bits)");
}
@@ -51,27 +50,21 @@ final class RuntimeDataWriter{
$this->value |= ($value << $this->offset);
$this->offset += $bits;
return $this;
}
/** @return $this */
public function writeBoundedInt(int $bits, int $min, int $max, int $value) : self{
public function boundedInt(int $bits, int $min, int $max, int $value) : void{
if($value < $min || $value > $max){
throw new \InvalidArgumentException("Value $value is outside the range $min - $max");
}
$this->writeInt($bits, $value - $min);
return $this;
$this->int($bits, $value - $min);
}
/** @return $this */
public function writeBool(bool $value) : self{
return $this->writeInt(1, $value ? 1 : 0);
public function bool(bool $value) : void{
$this->int(1, $value ? 1 : 0);
}
/** @return $this */
public function writeHorizontalFacing(int $facing) : self{
return $this->writeInt(2, match($facing){
public function horizontalFacing(int $facing) : void{
$this->int(2, match($facing){
Facing::NORTH => 0,
Facing::EAST => 1,
Facing::SOUTH => 2,
@@ -82,20 +75,16 @@ final class RuntimeDataWriter{
/**
* @param int[] $faces
*
* @return $this
*/
public function writeHorizontalFacingFlags(array $faces) : self{
public function horizontalFacingFlags(array $faces) : void{
$uniqueFaces = array_flip($faces);
foreach(Facing::HORIZONTAL as $facing){
$this->writeBool(isset($uniqueFaces[$facing]));
$this->bool(isset($uniqueFaces[$facing]));
}
return $this;
}
public function writeFacing(int $facing) : self{
return $this->writeInt(3, match($facing){
public function facing(int $facing) : void{
$this->int(3, match($facing){
0 => Facing::DOWN,
1 => Facing::UP,
2 => Facing::NORTH,
@@ -106,8 +95,12 @@ final class RuntimeDataWriter{
});
}
public function writeAxis(int $axis) : self{
return $this->writeInt(2, match($axis){
public function facingExcept(int $facing, int $except) : void{
$this->facing($facing);
}
public function axis(int $axis) : void{
$this->int(2, match($axis){
Axis::X => 0,
Axis::Z => 1,
Axis::Y => 2,
@@ -115,8 +108,8 @@ final class RuntimeDataWriter{
});
}
public function writeHorizontalAxis(int $axis) : self{
return $this->writeInt(1, match($axis){
public function horizontalAxis(int $axis) : void{
$this->int(1, match($axis){
Axis::X => 0,
Axis::Z => 1,
default => throw new \InvalidArgumentException("Invalid horizontal axis $axis")
@@ -127,36 +120,38 @@ final class RuntimeDataWriter{
* @param WallConnectionType[] $connections
* @phpstan-param array<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType> $connections
*/
public function writeWallConnections(array $connections) : self{
public function wallConnections(array $connections) : void{
//TODO: we can pack this into 7 bits instead of 8
foreach(Facing::HORIZONTAL as $facing){
$this->writeBoundedInt(2, 0, 2, match($connections[$facing] ?? null){
$this->boundedInt(2, 0, 2, match($connections[$facing] ?? null){
null => 0,
WallConnectionType::SHORT() => 1,
WallConnectionType::TALL() => 2,
default => throw new AssumptionFailedError("Unreachable")
});
}
return $this;
}
/**
* @param BrewingStandSlot[] $slots
* @phpstan-param array<int, BrewingStandSlot> $slots
*
* @return $this
*/
public function writeBrewingStandSlots(array $slots) : self{
public function brewingStandSlots(array $slots) : void{
foreach([
BrewingStandSlot::EAST(),
BrewingStandSlot::NORTHWEST(),
BrewingStandSlot::SOUTHWEST(),
] as $member){
$this->writeBool(isset($slots[$member->id()]));
$this->bool(isset($slots[$member->id()]));
}
}
return $this;
public function railShape(int $railShape) : void{
$this->int(4, $railShape);
}
public function straightOnlyRailShape(int $railShape) : void{
$this->int(3, $railShape);
}
public function getValue() : int{ return $this->value; }

View File

@@ -29,10 +29,10 @@ namespace pocketmine\data\runtime;
*/
trait RuntimeEnumDeserializerTrait{
abstract public function readInt(int $bits) : int;
abstract protected function readInt(int $bits) : int;
public function readBellAttachmentType() : \pocketmine\block\utils\BellAttachmentType{
return match($this->readInt(2)){
public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{
$value = match($this->readInt(2)){
0 => \pocketmine\block\utils\BellAttachmentType::CEILING(),
1 => \pocketmine\block\utils\BellAttachmentType::FLOOR(),
2 => \pocketmine\block\utils\BellAttachmentType::ONE_WALL(),
@@ -41,8 +41,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readCopperOxidation() : \pocketmine\block\utils\CopperOxidation{
return match($this->readInt(2)){
public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{
$value = match($this->readInt(2)){
0 => \pocketmine\block\utils\CopperOxidation::EXPOSED(),
1 => \pocketmine\block\utils\CopperOxidation::NONE(),
2 => \pocketmine\block\utils\CopperOxidation::OXIDIZED(),
@@ -51,8 +51,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readCoralType() : \pocketmine\block\utils\CoralType{
return match($this->readInt(3)){
public function coralType(\pocketmine\block\utils\CoralType &$value) : void{
$value = match($this->readInt(3)){
0 => \pocketmine\block\utils\CoralType::BRAIN(),
1 => \pocketmine\block\utils\CoralType::BUBBLE(),
2 => \pocketmine\block\utils\CoralType::FIRE(),
@@ -62,8 +62,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readDyeColor() : \pocketmine\block\utils\DyeColor{
return match($this->readInt(4)){
public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
$value = match($this->readInt(4)){
0 => \pocketmine\block\utils\DyeColor::BLACK(),
1 => \pocketmine\block\utils\DyeColor::BLUE(),
2 => \pocketmine\block\utils\DyeColor::BROWN(),
@@ -84,8 +84,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readLeverFacing() : \pocketmine\block\utils\LeverFacing{
return match($this->readInt(3)){
public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{
$value = match($this->readInt(3)){
0 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_X(),
1 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z(),
2 => \pocketmine\block\utils\LeverFacing::EAST(),
@@ -98,8 +98,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readMushroomBlockType() : \pocketmine\block\utils\MushroomBlockType{
return match($this->readInt(4)){
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
$value = match($this->readInt(4)){
0 => \pocketmine\block\utils\MushroomBlockType::ALL_CAP(),
1 => \pocketmine\block\utils\MushroomBlockType::CAP_EAST(),
2 => \pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE(),
@@ -115,8 +115,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readPotionType() : \pocketmine\item\PotionType{
return match($this->readInt(6)){
public function potionType(\pocketmine\item\PotionType &$value) : void{
$value = match($this->readInt(6)){
0 => \pocketmine\item\PotionType::AWKWARD(),
1 => \pocketmine\item\PotionType::FIRE_RESISTANCE(),
2 => \pocketmine\item\PotionType::HARMING(),
@@ -163,8 +163,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readSkullType() : \pocketmine\block\utils\SkullType{
return match($this->readInt(3)){
public function skullType(\pocketmine\block\utils\SkullType &$value) : void{
$value = match($this->readInt(3)){
0 => \pocketmine\block\utils\SkullType::CREEPER(),
1 => \pocketmine\block\utils\SkullType::DRAGON(),
2 => \pocketmine\block\utils\SkullType::PLAYER(),
@@ -175,8 +175,8 @@ trait RuntimeEnumDeserializerTrait{
};
}
public function readSlabType() : \pocketmine\block\utils\SlabType{
return match($this->readInt(2)){
public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
$value = match($this->readInt(2)){
0 => \pocketmine\block\utils\SlabType::BOTTOM(),
1 => \pocketmine\block\utils\SlabType::DOUBLE(),
2 => \pocketmine\block\utils\SlabType::TOP(),

View File

@@ -29,10 +29,10 @@ namespace pocketmine\data\runtime;
*/
trait RuntimeEnumSerializerTrait{
abstract public function writeInt(int $bits, int $value) : self;
abstract public function int(int $bits, int $value) : void;
public function writeBellAttachmentType(\pocketmine\block\utils\BellAttachmentType $value) : void{
$this->writeInt(2, match($value){
public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType $value) : void{
$this->int(2, match($value){
\pocketmine\block\utils\BellAttachmentType::CEILING() => 0,
\pocketmine\block\utils\BellAttachmentType::FLOOR() => 1,
\pocketmine\block\utils\BellAttachmentType::ONE_WALL() => 2,
@@ -41,8 +41,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeCopperOxidation(\pocketmine\block\utils\CopperOxidation $value) : void{
$this->writeInt(2, match($value){
public function copperOxidation(\pocketmine\block\utils\CopperOxidation $value) : void{
$this->int(2, match($value){
\pocketmine\block\utils\CopperOxidation::EXPOSED() => 0,
\pocketmine\block\utils\CopperOxidation::NONE() => 1,
\pocketmine\block\utils\CopperOxidation::OXIDIZED() => 2,
@@ -51,8 +51,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeCoralType(\pocketmine\block\utils\CoralType $value) : void{
$this->writeInt(3, match($value){
public function coralType(\pocketmine\block\utils\CoralType $value) : void{
$this->int(3, match($value){
\pocketmine\block\utils\CoralType::BRAIN() => 0,
\pocketmine\block\utils\CoralType::BUBBLE() => 1,
\pocketmine\block\utils\CoralType::FIRE() => 2,
@@ -62,8 +62,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeDyeColor(\pocketmine\block\utils\DyeColor $value) : void{
$this->writeInt(4, match($value){
public function dyeColor(\pocketmine\block\utils\DyeColor $value) : void{
$this->int(4, match($value){
\pocketmine\block\utils\DyeColor::BLACK() => 0,
\pocketmine\block\utils\DyeColor::BLUE() => 1,
\pocketmine\block\utils\DyeColor::BROWN() => 2,
@@ -84,8 +84,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeLeverFacing(\pocketmine\block\utils\LeverFacing $value) : void{
$this->writeInt(3, match($value){
public function leverFacing(\pocketmine\block\utils\LeverFacing $value) : void{
$this->int(3, match($value){
\pocketmine\block\utils\LeverFacing::DOWN_AXIS_X() => 0,
\pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z() => 1,
\pocketmine\block\utils\LeverFacing::EAST() => 2,
@@ -98,8 +98,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeMushroomBlockType(\pocketmine\block\utils\MushroomBlockType $value) : void{
$this->writeInt(4, match($value){
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType $value) : void{
$this->int(4, match($value){
\pocketmine\block\utils\MushroomBlockType::ALL_CAP() => 0,
\pocketmine\block\utils\MushroomBlockType::CAP_EAST() => 1,
\pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE() => 2,
@@ -115,8 +115,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writePotionType(\pocketmine\item\PotionType $value) : void{
$this->writeInt(6, match($value){
public function potionType(\pocketmine\item\PotionType $value) : void{
$this->int(6, match($value){
\pocketmine\item\PotionType::AWKWARD() => 0,
\pocketmine\item\PotionType::FIRE_RESISTANCE() => 1,
\pocketmine\item\PotionType::HARMING() => 2,
@@ -163,8 +163,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeSkullType(\pocketmine\block\utils\SkullType $value) : void{
$this->writeInt(3, match($value){
public function skullType(\pocketmine\block\utils\SkullType $value) : void{
$this->int(3, match($value){
\pocketmine\block\utils\SkullType::CREEPER() => 0,
\pocketmine\block\utils\SkullType::DRAGON() => 1,
\pocketmine\block\utils\SkullType::PLAYER() => 2,
@@ -175,8 +175,8 @@ trait RuntimeEnumSerializerTrait{
});
}
public function writeSlabType(\pocketmine\block\utils\SlabType $value) : void{
$this->writeInt(2, match($value){
public function slabType(\pocketmine\block\utils\SlabType $value) : void{
$this->int(2, match($value){
\pocketmine\block\utils\SlabType::BOTTOM() => 0,
\pocketmine\block\utils\SlabType::DOUBLE() => 1,
\pocketmine\block\utils\SlabType::TOP() => 2,