Type-hinted NBT tag constructors, made getValue() and setValue() more strict, fix dozens of assorted related bugs

This commit is contained in:
Dylan K. Taylor 2017-06-08 19:17:41 +01:00
parent 595e1ab52f
commit 890f72dbf2
24 changed files with 348 additions and 65 deletions

View File

@ -728,12 +728,12 @@ class Server{
}
$spawn = $this->getDefaultLevel()->getSafeSpawn();
$nbt = new CompoundTag("", [
new LongTag("firstPlayed", floor(microtime(true) * 1000)),
new LongTag("lastPlayed", floor(microtime(true) * 1000)),
new LongTag("firstPlayed", (int) (microtime(true) * 1000)),
new LongTag("lastPlayed", (int) (microtime(true) * 1000)),
new ListTag("Pos", [
new DoubleTag(0, $spawn->x),
new DoubleTag(1, $spawn->y),
new DoubleTag(2, $spawn->z)
new DoubleTag("", $spawn->x),
new DoubleTag("", $spawn->y),
new DoubleTag("", $spawn->z)
]),
new StringTag("Level", $this->getDefaultLevel()->getName()),
//new StringTag("SpawnLevel", $this->getDefaultLevel()->getName()),
@ -745,13 +745,13 @@ class Server{
new CompoundTag("Achievements", []),
new IntTag("playerGameType", $this->getGamemode()),
new ListTag("Motion", [
new DoubleTag(0, 0.0),
new DoubleTag(1, 0.0),
new DoubleTag(2, 0.0)
new DoubleTag("", 0.0),
new DoubleTag("", 0.0),
new DoubleTag("", 0.0)
]),
new ListTag("Rotation", [
new FloatTag(0, 0.0),
new FloatTag(1, 0.0)
new FloatTag("", 0.0),
new FloatTag("", 0.0)
]),
new FloatTag("FallDistance", 0.0),
new ShortTag("Fire", 0),

View File

@ -738,7 +738,7 @@ abstract class Entity extends Location implements Metadatable{
$this->namedtag->id = new StringTag("id", $this->getSaveId());
if($this->getNameTag() !== ""){
$this->namedtag->CustomName = new StringTag("CustomName", $this->getNameTag());
$this->namedtag->CustomNameVisible = new StringTag("CustomNameVisible", $this->isNameTagVisible());
$this->namedtag->CustomNameVisible = new ByteTag("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0);
}else{
unset($this->namedtag->CustomName);
unset($this->namedtag->CustomNameVisible);
@ -746,20 +746,20 @@ abstract class Entity extends Location implements Metadatable{
}
$this->namedtag->Pos = new ListTag("Pos", [
new DoubleTag(0, $this->x),
new DoubleTag(1, $this->y),
new DoubleTag(2, $this->z)
new DoubleTag("", $this->x),
new DoubleTag("", $this->y),
new DoubleTag("", $this->z)
]);
$this->namedtag->Motion = new ListTag("Motion", [
new DoubleTag(0, $this->motionX),
new DoubleTag(1, $this->motionY),
new DoubleTag(2, $this->motionZ)
new DoubleTag("", $this->motionX),
new DoubleTag("", $this->motionY),
new DoubleTag("", $this->motionZ)
]);
$this->namedtag->Rotation = new ListTag("Rotation", [
new FloatTag(0, $this->yaw),
new FloatTag(1, $this->pitch)
new FloatTag("", $this->yaw),
new FloatTag("", $this->pitch)
]);
$this->namedtag->FallDistance = new FloatTag("FallDistance", $this->fallDistance);
@ -771,7 +771,7 @@ abstract class Entity extends Location implements Metadatable{
if(count($this->effects) > 0){
$effects = [];
foreach($this->effects as $effect){
$effects[$effect->getId()] = new CompoundTag($effect->getId(), [
$effects[] = new CompoundTag("", [
"Id" => new ByteTag("Id", $effect->getId()),
"Amplifier" => new ByteTag("Amplifier", $effect->getAmplifier()),
"Duration" => new IntTag("Duration", $effect->getDuration()),

View File

@ -36,8 +36,11 @@ use pocketmine\Player;
class Item extends Entity{
const NETWORK_ID = 64;
protected $owner = null;
protected $thrower = null;
/** @var string */
protected $owner = "";
/** @var string */
protected $thrower = "";
/** @var int */
protected $pickupDelay = 0;
/** @var ItemItem */
protected $item;

View File

@ -32,6 +32,7 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\Timings;
use pocketmine\item\Item as ItemItem;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\network\mcpe\protocol\EntityEventPacket;
use pocketmine\utils\BlockIterator;
@ -51,10 +52,12 @@ abstract class Living extends Entity implements Damageable{
parent::initEntity();
if(isset($this->namedtag->HealF)){
$this->namedtag->Health = new ShortTag("Health", (int) $this->namedtag["HealF"]);
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag["HealF"]);
unset($this->namedtag->HealF);
}elseif(!isset($this->namedtag->Health) or !($this->namedtag->Health instanceof ShortTag)){
$this->namedtag->Health = new ShortTag("Health", $this->getMaxHealth());
}elseif(isset($this->namedtag->Health) and !($this->namedtag->Health instanceof FloatTag)){
$this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag->Health->getValue());
}else{
$this->namedtag->Health = new FloatTag("Health", (float) $this->getMaxHealth());
}
$this->setHealth($this->namedtag["Health"]);

View File

@ -59,7 +59,7 @@ abstract class BaseLevelProvider implements LevelProvider{
}
if(!isset($this->levelData->generatorName)){
$this->levelData->generatorName = new StringTag("generatorName", Generator::getGenerator("DEFAULT"));
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
}
if(!isset($this->levelData->generatorOptions)){

View File

@ -95,7 +95,7 @@ class LevelDB extends BaseLevelProvider{
if(isset($this->levelData->Generator)){
switch((int) $this->levelData->Generator->getValue()){ //Detect correct generator from MCPE data
case self::GENERATOR_FLAT:
$this->levelData->generatorName = new StringTag("generatorName", Generator::getGenerator("FLAT"));
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("FLAT"));
if(($layers = $this->db->get(self::ENTRY_FLAT_WORLD_LAYERS)) !== false){ //Detect existing custom flat layers
$layers = trim($layers, "[]");
}else{
@ -105,7 +105,7 @@ class LevelDB extends BaseLevelProvider{
break;
case self::GENERATOR_INFINITE:
//TODO: add a null generator which does not generate missing chunks (to allow importing back to MCPE and generating more normal terrain without PocketMine messing things up)
$this->levelData->generatorName = new StringTag("generatorName", Generator::getGenerator("DEFAULT"));
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
break;
case self::GENERATOR_LIMITED:
@ -114,7 +114,7 @@ class LevelDB extends BaseLevelProvider{
throw new LevelException("Unknown LevelDB world format type, this level cannot be loaded");
}
}else{
$this->levelData->generatorName = new StringTag("generatorName", Generator::getGenerator("DEFAULT"));
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
}
}

View File

@ -56,7 +56,7 @@ class Anvil extends McRegion{
if($subChunk->isEmpty()){
continue;
}
$nbt->Sections[++$subChunks] = new CompoundTag(null, [
$nbt->Sections[++$subChunks] = new CompoundTag("", [
"Y" => new ByteTag("Y", $y),
"Blocks" => new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY
"Data" => new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())),

View File

@ -59,7 +59,7 @@ class PMAnvil extends Anvil{
if($subChunk->isEmpty()){
continue;
}
$nbt->Sections[++$subChunks] = new CompoundTag(null, [
$nbt->Sections[++$subChunks] = new CompoundTag("", [
"Y" => new ByteTag("Y", $y),
"Blocks" => new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
"Data" => new ByteArrayTag("Data", $subChunk->getBlockDataArray()),

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class ByteArrayTag extends NamedTag{
/**
* ByteArrayTag constructor.
*
* @param string $name
* @param string $value
*/
public function __construct(string $name = "", string $value = ""){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_ByteArray;
}
@ -41,4 +51,23 @@ class ByteArrayTag extends NamedTag{
$nbt->putInt(strlen($this->value), $network);
$nbt->put($this->value);
}
/**
* @return string
*/
public function &getValue() : string{
return parent::getValue(); // TODO: Change the autogenerated stub
}
/**
* @param string $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_string($value)){
throw new \TypeError("ByteArrayTag value must be of type string, " . gettype($value) . " given");
}
parent::setValue($value);
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class ByteTag extends NamedTag{
/**
* ByteTag constructor.
*
* @param string $name
* @param int $value
*/
public function __construct(string $name = "", int $value = 0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Byte;
}
@ -40,4 +50,25 @@ class ByteTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putByte($this->value);
}
/**
* @return int
*/
public function &getValue() : int{
return parent::getValue();
}
/**
* @param int $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_int($value)){
throw new \TypeError("ByteTag value must be of type int, " . gettype($value) . " given");
}elseif($value < -(2 ** 7) or $value > ((2 ** 7) - 1)){
throw new \InvalidArgumentException("Value $value is too large!");
}
parent::setValue($value);
}
}

View File

@ -30,14 +30,13 @@ use pocketmine\nbt\NBT;
class CompoundTag extends NamedTag implements \ArrayAccess{
/**
* CompoundTag constructor.
*
* @param string $name
* @param NamedTag[] $value
*/
public function __construct($name = "", $value = []){
$this->__name = $name;
foreach($value as $tag){
$this->{$tag->getName()} = $tag;
}
public function __construct(string $name = "", array $value = []){
parent::__construct($name, $value);
}
public function getCount(){
@ -51,13 +50,22 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
return $count;
}
/**
* @param NamedTag[] $value
*
* @throws \TypeError
*/
public function setValue($value){
if(is_array($value)){
foreach($value as $name => $tag){
if($tag instanceof NamedTag){
$this->{$name} = $tag;
$this->{$tag->getName()} = $tag;
}else{
throw new \TypeError("CompoundTag members must be NamedTags, got " . gettype($tag) . " in given array");
}
}
}else{
throw new \TypeError("CompoundTag value must be NamedTag[], " . gettype($value) . " given");
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class DoubleTag extends NamedTag{
/**
* DoubleTag constructor.
*
* @param string $name
* @param float $value
*/
public function __construct(string $name = "", float $value = 0.0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Double;
}
@ -40,4 +50,23 @@ class DoubleTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putDouble($this->value);
}
/**
* @return float
*/
public function &getValue() : float{
return parent::getValue();
}
/**
* @param float $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_float($value) and !is_int($value)){
throw new \TypeError("DoubleTag value must be of type double, " . gettype($value) . " given");
}
parent::setValue((float) $value);
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class FloatTag extends NamedTag{
/**
* FloatTag constructor.
*
* @param string $name
* @param float $value
*/
public function __construct(string $name = "", float $value = 0.0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Float;
}
@ -40,4 +50,18 @@ class FloatTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putFloat($this->value);
}
/**
* @return float
*/
public function &getValue() : float{
return parent::getValue();
}
public function setValue($value){
if(!is_float($value) and !is_int($value)){
throw new \TypeError("FloatTag value must be of type float, " . gettype($value) . " given");
}
parent::setValue((float) $value);
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class IntArrayTag extends NamedTag{
/**
* IntArrayTag constructor.
*
* @param string $name
* @param int[] $value
*/
public function __construct(string $name = "", array $value = []){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_IntArray;
}
@ -48,4 +58,27 @@ class IntArrayTag extends NamedTag{
$str .= implode(", ", $this->value);
return $str . "}";
}
/**
* @return int[]
*/
public function &getValue() : array{
return parent::getValue();
}
/**
* @param int[] $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_array($value)){
throw new \TypeError("IntArrayTag value must be of type int[], " . gettype($value) . " given");
}
assert(count(array_filter($value, function($v){
return !is_int($v);
})) === 0);
parent::setValue($value);
}
}

View File

@ -29,6 +29,14 @@ use pocketmine\nbt\NBT;
class IntTag extends NamedTag{
/**
* @param string $name
* @param int $value
*/
public function __construct(string $name = "", int $value = 0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Int;
}
@ -40,4 +48,25 @@ class IntTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putInt($this->value, $network);
}
/**
* @return int
*/
public function &getValue() : int{
return parent::getValue();
}
/**
* @param int $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_int($value)){
throw new \TypeError("IntTag value must be of type int, " . gettype($value) . " given");
}elseif($value < -(2 ** 31) or $value > ((2 ** 31) - 1)){
throw new \InvalidArgumentException("Value $value is too large!");
}
parent::setValue($value);
}
}

View File

@ -31,14 +31,20 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
private $tagType = NBT::TAG_End;
public function __construct($name = "", $value = []){
$this->__name = $name;
foreach($value as $k => $v){
$this->{$k} = $v;
}
/**
* ListTag constructor.
*
* @param string $name
* @param NamedTag[] $value
*/
public function __construct(string $name = "", array $value = []){
parent::__construct($name, $value);
}
public function &getValue(){
/**
* @return NamedTag[]
*/
public function &getValue() : array{
$value = [];
foreach($this as $k => $v){
if($v instanceof Tag){
@ -49,13 +55,22 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
return $value;
}
/**
* @param NamedTag[] $value
*
* @throws \TypeError
*/
public function setValue($value){
if(is_array($value)){
foreach($value as $name => $tag){
if($tag instanceof NamedTag){
$this->{$name} = $tag;
}else{
throw new \TypeError("ListTag members must be NamedTags, got " . gettype($tag) . " in given array");
}
}
}else{
throw new \TypeError("ListTag value must be NamedTag[], " . gettype($value) . " given");
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class LongTag extends NamedTag{
/**
* LongTag constructor.
*
* @param string $name
* @param int $value
*/
public function __construct(string $name = "", int $value = 0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Long;
}

View File

@ -29,13 +29,13 @@ abstract class NamedTag extends Tag{
protected $__name;
/**
* @param string $name
* @param bool|float|double|int|ByteTag|ShortTag|array|CompoundTag|ListTag|string $value
* @param string $name
* @param mixed $value
*/
public function __construct($name = "", $value = null){
public function __construct(string $name = "", $value = null){
$this->__name = ($name === null or $name === false) ? "" : $name;
if($value !== null){
$this->value = $value;
$this->setValue($value);
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class ShortTag extends NamedTag{
/**
* ShortTag constructor.
*
* @param string $name
* @param int $value
*/
public function __construct(string $name = "", int $value = 0){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_Short;
}
@ -40,4 +50,25 @@ class ShortTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putShort($this->value);
}
/**
* @return int
*/
public function &getValue() : int{
return parent::getValue(); // TODO: Change the autogenerated stub
}
/**
* @param int $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_int($value)){
throw new \TypeError("ShortTag value must be of type int, " . gettype($value) . " given");
}elseif($value < -(2 ** 15) or $value > ((2 ** 15) - 1)){
throw new \InvalidArgumentException("Value $value is too large!");
}
parent::setValue($value);
}
}

View File

@ -29,6 +29,16 @@ use pocketmine\nbt\NBT;
class StringTag extends NamedTag{
/**
* StringTag constructor.
*
* @param string $name
* @param string $value
*/
public function __construct(string $name = "", string $value = ""){
parent::__construct($name, $value);
}
public function getType(){
return NBT::TAG_String;
}
@ -40,4 +50,23 @@ class StringTag extends NamedTag{
public function write(NBT $nbt, bool $network = false){
$nbt->putString($this->value, $network);
}
/**
* @return string
*/
public function &getValue() : string{
return parent::getValue();
}
/**
* @param string $value
*
* @throws \TypeError
*/
public function setValue($value){
if(!is_string($value)){
throw new \TypeError("ShortTag value must be of type int, " . gettype($value) . " given");
}
parent::setValue($value);
}
}

View File

@ -190,15 +190,24 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
}
public function getName(){
/**
* @return string
*/
public function getName() : string{
return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Chest";
}
public function hasName(){
/**
* @return bool
*/
public function hasName() : bool{
return isset($this->namedtag->CustomName);
}
public function setName($str){
/**
* @param string $str
*/
public function setName(string $str){
if($str === ""){
unset($this->namedtag->CustomName);
return;

View File

@ -30,15 +30,15 @@ use pocketmine\nbt\tag\StringTag;
class EnchantTable extends Spawnable implements Nameable{
public function getName(){
public function getName() : string{
return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Enchanting Table";
}
public function hasName(){
public function hasName() : bool{
return isset($this->namedtag->CustomName);
}
public function setName($str){
public function setName(string $str){
if($str === ""){
unset($this->namedtag->CustomName);
return;

View File

@ -72,15 +72,15 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
}
}
public function getName(){
public function getName() : string{
return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Furnace";
}
public function hasName(){
public function hasName() : bool{
return isset($this->namedtag->CustomName);
}
public function setName($str){
public function setName(string $str){
if($str === ""){
unset($this->namedtag->CustomName);
return;
@ -228,11 +228,11 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
}
if($this->namedtag["BurnTime"] > 0){
$this->namedtag->BurnTime = new ShortTag("BurnTime", $this->namedtag["BurnTime"] - 1);
$this->namedtag->BurnTime = new ShortTag("BurnTime", ((int) $this->namedtag["BurnTime"]) - 1);
$this->namedtag->BurnTicks = new ShortTag("BurnTicks", (int) ceil(($this->namedtag["BurnTime"] / $this->namedtag["MaxTime"] * 200)));
if($smelt instanceof FurnaceRecipe and $canSmelt){
$this->namedtag->CookTime = new ShortTag("CookTime", $this->namedtag["CookTime"] + 1);
$this->namedtag->CookTime = new ShortTag("CookTime", (int) ($this->namedtag["CookTime"]) + 1);
if($this->namedtag["CookTime"] >= 200){ //10 seconds
$product = Item::get($smelt->getResult()->getId(), $smelt->getResult()->getDamage(), $product->getCount() + 1);
@ -247,7 +247,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
$this->inventory->setSmelting($raw);
}
$this->namedtag->CookTime = new ShortTag("CookTime", $this->namedtag["CookTime"] - 200);
$this->namedtag->CookTime = new ShortTag("CookTime", ((int) $this->namedtag["CookTime"]) - 200);
}
}elseif($this->namedtag["BurnTime"] <= 0){
$this->namedtag->BurnTime = new ShortTag("BurnTime", 0);
@ -297,8 +297,8 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
new IntTag("x", (int) $this->x),
new IntTag("y", (int) $this->y),
new IntTag("z", (int) $this->z),
new ShortTag("BurnTime", $this->namedtag["BurnTime"]),
new ShortTag("CookTime", $this->namedtag["CookTime"])
new ShortTag("BurnTime", (int) $this->namedtag["BurnTime"]),
new ShortTag("CookTime", (int) $this->namedtag["CookTime"])
]);
if($this->hasName()){

View File

@ -30,15 +30,15 @@ interface Nameable{
/**
* @return string
*/
public function getName();
public function getName() : string;
/**
* @param void $str
* @param string $str
*/
public function setName($str);
public function setName(string $str);
/**
* @return bool
*/
public function hasName();
public function hasName() : bool;
}