Merge branch 'master' into mcpe-1.2.5

This commit is contained in:
Dylan K. Taylor 2017-10-31 21:52:16 +00:00
commit dcdea6a1f4
25 changed files with 249 additions and 225 deletions

View File

@ -762,6 +762,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/**
* {@inheritdoc}
*
* If null is given, will additionally send the skin to the player itself as well as its viewers.
*/
public function sendSkin(array $targets = null) : void{
parent::sendSkin($targets ?? $this->server->getOnlinePlayers());
}
/**
* Gets the player IP address
*
@ -1988,24 +1997,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/* Mojang, some stupid reason, send every single model for every single skin in the selected skin-pack.
* Not only that, they are pretty-printed. This decode/encode is to get rid of the pretty-print, which cuts down
* significantly on the amount of wasted bytes.
* TODO: find out what model crap can be safely dropped from the packet (unless it gets fixed first)
*/
$geometryJsonEncoded = base64_decode($packet->clientData["SkinGeometry"] ?? "");
if($geometryJsonEncoded !== ""){
$geometryJsonEncoded = json_encode(json_decode($geometryJsonEncoded));
}
$skin = new Skin(
$packet->clientData["SkinId"],
base64_decode($packet->clientData["SkinData"] ?? ""),
base64_decode($packet->clientData["CapeData"] ?? ""),
$packet->clientData["SkinGeometryName"],
$geometryJsonEncoded
base64_decode($packet->clientData["SkinGeometry"] ?? "")
);
$skin->debloatGeometryData();
if(!$skin->isValid()){
$this->close("", "disconnectionScreen.invalidSkin");
@ -2911,7 +2910,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z));
$tile = $this->level->getTileAt($packet->x, $packet->y, $packet->z);
if($tile instanceof ItemFrame){
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $tile->getBlock(), null, 5 - $tile->getBlock()->getDamage(), PlayerInteractEvent::LEFT_CLICK_BLOCK);
$this->server->getPluginManager()->callEvent($ev);

View File

@ -410,6 +410,12 @@ namespace pocketmine {
return -1;
}
/**
* @param int $start
* @param array|null $trace
*
* @return array
*/
function getTrace($start = 0, $trace = null){
if($trace === null){
if(function_exists("xdebug_get_function_stack")){

View File

@ -2156,6 +2156,10 @@ class Server{
}
}
/**
* @param \Throwable $e
* @param array|null $trace
*/
public function exceptionHandler(\Throwable $e, $trace = null){
if($e === null){
return;

View File

@ -126,16 +126,20 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
$this->skin = $skin;
$this->skin->debloatGeometryData();
}
/**
* @param Player[] $targets
* Sends the human's skin to the specified list of players. If null is given for targets, the skin will be sent to
* all viewers.
*
* @param Player[]|null $targets
*/
public function sendSkin(array $targets) : void{
public function sendSkin(array $targets = null) : void{
$pk = new PlayerSkinPacket();
$pk->uuid = $this->getUniqueId();
$pk->skin = $this->skin;
$this->server->broadcastPacket($targets, $pk);
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
}
public function jump(){

View File

@ -87,4 +87,17 @@ class Skin{
return $this->geometryData;
}
/**
* Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON.
*
* Mojang, some stupid reason, send every single model for every single skin in the selected skin-pack.
* Not only that, they are pretty-printed.
* TODO: find out what model crap can be safely dropped from the packet (unless it gets fixed first)
*/
public function debloatGeometryData() : void{
if($this->geometryData !== ""){
$this->geometryData = (string) json_encode(json_decode($this->geometryData));
}
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\inventory;
use pocketmine\event\Timings;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\mcpe\protocol\BatchPacket;
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\Server;
use pocketmine\utils\Config;
@ -35,19 +36,18 @@ use pocketmine\utils\UUID;
class CraftingManager{
/** @var CraftingRecipe[] */
public $recipes = [];
protected $recipes = [];
/** @var ShapedRecipe[][] */
protected $shapedRecipes = [];
/** @var ShapelessRecipe[][] */
protected $shapelessRecipes = [];
/** @var FurnaceRecipe[] */
public $furnaceRecipes = [];
protected $furnaceRecipes = [];
private static $RECIPE_COUNT = 0;
/** @var CraftingDataPacket */
/** @var BatchPacket */
private $craftingDataCache;
public function __construct(){
@ -113,16 +113,21 @@ class CraftingManager{
$pk->encode();
$this->craftingDataCache = $pk;
$batch = new BatchPacket();
$batch->addPacket($pk);
$batch->setCompressionLevel(Server::getInstance()->networkCompressionLevel);
$batch->encode();
$this->craftingDataCache = $batch;
Timings::$craftingDataCacheRebuildTimer->stopTiming();
}
/**
* Returns a CraftingDataPacket for sending to players. Rebuilds the cache if it is outdated.
* Returns a pre-compressed CraftingDataPacket for sending to players. Rebuilds the cache if it is not found.
*
* @return CraftingDataPacket
* @return BatchPacket
*/
public function getCraftingDataPacket() : CraftingDataPacket{
public function getCraftingDataPacket() : BatchPacket{
if($this->craftingDataCache === null){
$this->buildCraftingDataCache();
}
@ -230,7 +235,7 @@ class CraftingManager{
/** @var Item[] $row */
foreach($map as $y => $row){
foreach($row as $x => $item){
$item = clone $item;
$map[$y][$x] = clone $item;
}
}

View File

@ -26,6 +26,14 @@ namespace pocketmine\inventory;
use pocketmine\utils\UUID;
class MultiRecipe{
const TYPE_REPAIR_ITEM = "00000000-0000-0000-0000-000000000001";
const TYPE_MAP_EXTENDING = "D392B075-4BA1-40AE-8789-AF868D56F6CE";
const TYPE_MAP_CLONING = "85939755-BA10-4D9D-A4CC-EFB7A8E943C4";
const TYPE_MAP_UPGRADING = "AECD2294-4B94-434B-8667-4499BB2C9327";
const TYPE_BOOK_CLONING = "D1CA6B84-338E-4F2F-9C6B-76CC8B4BD98D";
const TYPE_BANNER_DUPLICATE = "B5C5D105-75A2-4076-AF2B-923EA2BF4BF0";
const TYPE_BANNER_ADD_PATTERN = "D81AAEAF-E172-4440-9225-868DF030D27B";
const TYPE_FIREWORKS = "00000000-0000-0000-0000-000000000002";
private $uuid;

View File

@ -211,7 +211,7 @@ class Explosion{
$this->level->setBlockIdAt($block->x, $block->y, $block->z, 0);
$t = $this->level->getTile($block);
$t = $this->level->getTileAt($block->x, $block->y, $block->z);
if($t instanceof Tile){
if($yieldDrops and $t instanceof Container){
if($t instanceof Chest){

View File

@ -1962,17 +1962,33 @@ class Level implements ChunkManager, Metadatable{
}
/**
* Returns the Tile in a position, or null if not found
* Returns the Tile in a position, or null if not found.
*
* Note: This method wraps getTileAt(). If you're guaranteed to be passing integers, and you're using this method
* in performance-sensitive code, consider using getTileAt() instead of this method for better performance.
*
* @param Vector3 $pos
*
* @return Tile|null
*/
public function getTile(Vector3 $pos){
$chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false);
public function getTile(Vector3 $pos) : ?Tile{
return $this->getTileAt((int) floor($pos->x), (int) floor($pos->y), (int) floor($pos->z));
}
/**
* Returns the tile at the specified x,y,z coordinates, or null if it does not exist.
*
* @param int $x
* @param int $y
* @param int $z
*
* @return Tile|null
*/
public function getTileAt(int $x, int $y, int $z) : ?Tile{
$chunk = $this->getChunk($x >> 4, $z >> 4);
if($chunk !== null){
return $chunk->getTile($pos->x & 0x0f, $pos->y, $pos->z & 0x0f);
return $chunk->getTile($x & 0x0f, $y, $z & 0x0f);
}
return null;

View File

@ -54,13 +54,9 @@ class Anvil extends McRegion{
if($subChunk->isEmpty()){
continue;
}
$nbt->Sections[++$subChunks] = new CompoundTag("", [
new ByteTag("Y", $y),
new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY
new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())),
new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")),
new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray()))
]);
$tag = $this->serializeSubChunk($subChunk);
$tag->setByte("Y", $y);
$nbt->Sections[++$subChunks] = $tag;
}
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray());
@ -94,6 +90,15 @@ class Anvil extends McRegion{
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
}
protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag{
return new CompoundTag("", [
new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY
new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())),
new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")),
new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray()))
]);
}
public function nbtDeserialize(string $data){
$nbt = new NBT(NBT::BIG_ENDIAN);
try{
@ -111,12 +116,7 @@ class Anvil extends McRegion{
if($chunk->Sections instanceof ListTag){
foreach($chunk->Sections as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->Y->getValue()] = new SubChunk(
ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue())
);
$subChunks[$subChunk->Y->getValue()] = $this->deserializeSubChunk($subChunk);
}
}
}
@ -148,6 +148,15 @@ class Anvil extends McRegion{
}
}
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue())
);
}
public static function getProviderName() : string{
return "anvil";
}

View File

@ -40,108 +40,22 @@ class PMAnvil extends Anvil{
const REGION_FILE_EXTENSION = "mcapm";
public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ());
$nbt->V = new ByteTag("V", 1);
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
$nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->Sections = new ListTag("Sections", [], NBT::TAG_Compound);
$subChunks = -1;
foreach($chunk->getSubChunks() as $y => $subChunk){
if($subChunk->isEmpty()){
continue;
}
$nbt->Sections[++$subChunks] = new CompoundTag("", [
new ByteTag("Y", $y),
new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
new ByteArrayTag("Data", $subChunk->getBlockDataArray()),
new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()),
new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray())
]);
}
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray());
$nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray());
$entities = [];
foreach($chunk->getEntities() as $entity){
if($entity->canSaveWithChunk() and !$entity->isClosed()){
$entity->saveNBT();
$entities[] = $entity->namedtag;
}
}
$nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound);
$tiles = [];
foreach($chunk->getTiles() as $tile){
$tile->saveNBT();
$tiles[] = $tile->namedtag;
}
$nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound);
//TODO: TileTicks
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
$writer->setData(new CompoundTag("", [$nbt]));
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag{
return new CompoundTag("", [
new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
new ByteArrayTag("Data", $subChunk->getBlockDataArray()),
new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()),
new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray())
]);
}
public function nbtDeserialize(string $data){
$nbt = new NBT(NBT::BIG_ENDIAN);
try{
$nbt->readCompressed($data);
$chunk = $nbt->getData();
if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){
throw new ChunkException("Invalid NBT format");
}
$chunk = $chunk->Level;
$subChunks = [];
if($chunk->Sections instanceof ListTag){
foreach($chunk->Sections as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->Y->getValue()] = new SubChunk(
$subChunk->Blocks->getValue(),
$subChunk->Data->getValue(),
$subChunk->SkyLight->getValue(),
$subChunk->BlockLight->getValue()
);
}
}
}
$result = new Chunk(
$chunk["xPos"],
$chunk["zPos"],
$subChunks,
isset($chunk->Entities) ? $chunk->Entities->getValue() : [],
isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [],
isset($chunk->Biomes) ? $chunk->Biomes->getValue() : "",
isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : []
);
$result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false);
$result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false);
$result->setGenerated(true);
return $result;
}catch(\Throwable $e){
MainLogger::getLogger()->logException($e);
return null;
}
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
$subChunk->Blocks->getValue(),
$subChunk->Data->getValue(),
$subChunk->SkyLight->getValue(),
$subChunk->BlockLight->getValue()
);
}
public static function getProviderName() : string{

View File

@ -151,22 +151,24 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
/**
* Returns the value of the child tag with the specified name, or $default if the tag doesn't exist. If the child
* tag is not of type $expectedType, an exception will be thrown.
* tag is not of type $expectedType, an exception will be thrown, unless a default is given and $badTagDefault is
* true.
*
* @param string $name
* @param string $expectedClass
* @param mixed $default
* @param bool $badTagDefault Return the specified default if the tag is not of the expected type.
*
* @return mixed
*/
public function getTagValue(string $name, string $expectedClass, $default = null){
$tag = $this->getTag($name, $expectedClass);
if($tag !== null){
public function getTagValue(string $name, string $expectedClass, $default = null, bool $badTagDefault = false){
$tag = $this->getTag($name, $badTagDefault ? NamedTag::class : $expectedClass);
if($tag instanceof $expectedClass){
return $tag->getValue();
}
if($default === null){
throw new \RuntimeException("Tag with name \"$name\" not found and no default value given");
throw new \RuntimeException("Tag with name \"$name\" " . ($tag !== null ? "not of expected type" : "not found") . " and no valid default value given");
}
return $default;
@ -179,91 +181,100 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
/**
* @param string $name
* @param int|null $default
* @param bool $badTagDefault
*
* @return int
*/
public function getByte(string $name, ?int $default = null) : int{
return $this->getTagValue($name, ByteTag::class, $default);
public function getByte(string $name, ?int $default = null, bool $badTagDefault = false) : int{
return $this->getTagValue($name, ByteTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param int|null $default
* @param bool $badTagDefault
*
* @return int
*/
public function getShort(string $name, ?int $default = null) : int{
return $this->getTagValue($name, ShortTag::class, $default);
public function getShort(string $name, ?int $default = null, bool $badTagDefault = false) : int{
return $this->getTagValue($name, ShortTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param int|null $default
* @param bool $badTagDefault
*
* @return int
*/
public function getInt(string $name, ?int $default = null) : int{
return $this->getTagValue($name, IntTag::class, $default);
public function getInt(string $name, ?int $default = null, bool $badTagDefault = false) : int{
return $this->getTagValue($name, IntTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param int|null $default
* @param bool $badTagDefault
*
* @return int
*/
public function getLong(string $name, ?int $default = null) : int{
return $this->getTagValue($name, LongTag::class, $default);
public function getLong(string $name, ?int $default = null, bool $badTagDefault = false) : int{
return $this->getTagValue($name, LongTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param float|null $default
* @param bool $badTagDefault
*
* @return float
*/
public function getFloat(string $name, ?float $default = null) : float{
return $this->getTagValue($name, FloatTag::class, $default);
public function getFloat(string $name, ?float $default = null, bool $badTagDefault = false) : float{
return $this->getTagValue($name, FloatTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param float|null $default
* @param bool $badTagDefault
*
* @return float
*/
public function getDouble(string $name, ?float $default = null) : float{
return $this->getTagValue($name, DoubleTag::class, $default);
public function getDouble(string $name, ?float $default = null, bool $badTagDefault = false) : float{
return $this->getTagValue($name, DoubleTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param null|string $default
* @param string|null $default
* @param bool $badTagDefault
*
* @return string
*/
public function getByteArray(string $name, ?string $default = null) : string{
return $this->getTagValue($name, ByteArrayTag::class, $default);
public function getByteArray(string $name, ?string $default = null, bool $badTagDefault = false) : string{
return $this->getTagValue($name, ByteArrayTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param null|string $default
* @param string|null $default
* @param bool $badTagDefault
*
* @return string
*/
public function getString(string $name, ?string $default = null) : string{
return $this->getTagValue($name, StringTag::class, $default);
public function getString(string $name, ?string $default = null, bool $badTagDefault = false) : string{
return $this->getTagValue($name, StringTag::class, $default, $badTagDefault);
}
/**
* @param string $name
* @param int[]|null $default
* @param bool $badTagDefault
*
* @return int[]
*/
public function getIntArray(string $name, ?array $default = null) : array{
return $this->getTagValue($name, IntArrayTag::class, $default);
public function getIntArray(string $name, ?array $default = null, bool $badTagDefault = false) : array{
return $this->getTagValue($name, IntArrayTag::class, $default, $badTagDefault);
}
/**
@ -273,11 +284,12 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
* @param string $name Name of the tag to set
* @param string $tagClass Class that extends NamedTag
* @param mixed $value Value to set. This should be compatible with the specified tag type.
* @param bool $force Force set the value even if the existing tag is not the correct type (overwrite the old tag)
*/
public function setTagValue(string $name, string $tagClass, $value) : void{
public function setTagValue(string $name, string $tagClass, $value, bool $force) : void{
assert(is_a($tagClass, NamedTag::class, true));
$tag = $this->getTag($name, $tagClass);
if($tag !== null){
$tag = $this->getTag($name, $force ? NamedTag::class : $tagClass);
if($tag instanceof $tagClass){
$tag->setValue($value);
}else{
$this->setTag(new $tagClass($name, $value));
@ -291,73 +303,82 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
/**
* @param string $name
* @param int $value
* @param bool $force
*/
public function setByte(string $name, int $value) : void{
$this->setTagValue($name, ByteTag::class, $value);
public function setByte(string $name, int $value, bool $force = false) : void{
$this->setTagValue($name, ByteTag::class, $value, $force);
}
/**
* @param string $name
* @param int $value
* @param bool $force
*/
public function setShort(string $name, int $value) : void{
$this->setTagValue($name, ShortTag::class, $value);
public function setShort(string $name, int $value, bool $force = false) : void{
$this->setTagValue($name, ShortTag::class, $value, $force);
}
/**
* @param string $name
* @param int $value
* @param bool $force
*/
public function setInt(string $name, int $value) : void{
$this->setTagValue($name, IntTag::class, $value);
public function setInt(string $name, int $value, bool $force = false) : void{
$this->setTagValue($name, IntTag::class, $value, $force);
}
/**
* @param string $name
* @param int $value
* @param bool $force
*/
public function setLong(string $name, int $value) : void{
$this->setTagValue($name, LongTag::class, $value);
public function setLong(string $name, int $value, bool $force = false) : void{
$this->setTagValue($name, LongTag::class, $value, $force);
}
/**
* @param string $name
* @param float $value
* @param bool $force
*/
public function setFloat(string $name, float $value) : void{
$this->setTagValue($name, FloatTag::class, $value);
public function setFloat(string $name, float $value, bool $force = false) : void{
$this->setTagValue($name, FloatTag::class, $value, $force);
}
/**
* @param string $name
* @param float $value
* @param bool $force
*/
public function setDouble(string $name, float $value) : void{
$this->setTagValue($name, DoubleTag::class, $value);
public function setDouble(string $name, float $value, bool $force = false) : void{
$this->setTagValue($name, DoubleTag::class, $value, $force);
}
/**
* @param string $name
* @param string $value
* @param bool $force
*/
public function setByteArray(string $name, string $value) : void{
$this->setTagValue($name, ByteArrayTag::class, $value);
public function setByteArray(string $name, string $value, bool $force = false) : void{
$this->setTagValue($name, ByteArrayTag::class, $value, $force);
}
/**
* @param string $name
* @param string $value
* @param bool $force
*/
public function setString(string $name, string $value) : void{
$this->setTagValue($name, StringTag::class, $value);
public function setString(string $name, string $value, bool $force = false) : void{
$this->setTagValue($name, StringTag::class, $value, $force);
}
/**
* @param string $name
* @param int[] $value
* @param bool $force
*/
public function setIntArray(string $name, array $value) : void{
$this->setTagValue($name, IntArrayTag::class, $value);
public function setIntArray(string $name, array $value, bool $force = false) : void{
$this->setTagValue($name, IntArrayTag::class, $value, $force);
}

View File

@ -31,17 +31,17 @@ class EntityPickRequestPacket extends DataPacket{
const NETWORK_ID = ProtocolInfo::ENTITY_PICK_REQUEST_PACKET;
/** @var int */
public $entityTypeId;
public $entityUniqueId;
/** @var int */
public $hotbarSlot;
protected function decodePayload(){
$this->entityTypeId = $this->getLLong();
$this->entityUniqueId = $this->getLLong();
$this->hotbarSlot = $this->getByte();
}
protected function encodePayload(){
$this->putLLong($this->entityTypeId);
$this->putLLong($this->entityUniqueId);
$this->putByte($this->hotbarSlot);
}

View File

@ -45,5 +45,6 @@ interface WindowTypes{
const STRUCTURE_EDITOR = 14;
const TRADING = 15;
const COMMAND_BLOCK = 16;
const JUKEBOX = 17;
}

View File

@ -35,8 +35,8 @@ class Bed extends Spawnable{
const TAG_COLOR = "color";
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_COLOR, ByteTag::class)){
$nbt->setTag(new ByteTag(self::TAG_COLOR, 14)); //default to old red
if(!$nbt->hasTag(self::TAG_COLOR, ByteTag::class)){ //TODO: check PC format
$nbt->setByte(self::TAG_COLOR, 14, true); //default to old red
}
parent::__construct($level, $nbt);
}

View File

@ -136,7 +136,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
*/
public function getPair() : ?Chest{
if($this->isPaired()){
$tile = $this->getLevel()->getTile(new Vector3($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ)));
$tile = $this->getLevel()->getTileAt($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ));
if($tile instanceof Chest){
return $tile;
}
@ -152,8 +152,8 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
$this->createPair($tile);
$this->spawnToAll();
$tile->spawnToAll();
$this->onChanged();
$tile->onChanged();
$this->checkPairing();
return true;
@ -175,12 +175,12 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
$tile = $this->getPair();
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
$this->spawnToAll();
$this->onChanged();
if($tile instanceof Chest){
$tile->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
$tile->checkPairing();
$tile->spawnToAll();
$tile->onChanged();
}
$this->checkPairing();

View File

@ -37,11 +37,12 @@ class FlowerPot extends Spawnable{
const TAG_ITEM_DATA = "mData";
public function __construct(Level $level, CompoundTag $nbt){
//TODO: check PC format
if(!$nbt->hasTag(self::TAG_ITEM, ShortTag::class)){
$nbt->setTag(new ShortTag(self::TAG_ITEM, 0));
$nbt->setShort(self::TAG_ITEM, 0, true);
}
if(!$nbt->hasTag(self::TAG_ITEM_DATA, IntTag::class)){
$nbt->setTag(new IntTag(self::TAG_ITEM_DATA, 0));
$nbt->setInt(self::TAG_ITEM_DATA, 0, true);
}
parent::__construct($level, $nbt);
}

View File

@ -54,7 +54,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_BURN_TIME, ShortTag::class) or $nbt->getShort(self::TAG_BURN_TIME) < 0){
$nbt->setTag(new ShortTag(self::TAG_BURN_TIME, 0));
$nbt->setShort(self::TAG_BURN_TIME, 0, true);
}
if(
@ -62,16 +62,16 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
$nbt->getShort(self::TAG_COOK_TIME) < 0 or
($nbt->getShort(self::TAG_BURN_TIME) === 0 and $nbt->getShort(self::TAG_COOK_TIME) > 0)
){
$nbt->setTag(new ShortTag(self::TAG_COOK_TIME, 0));
$nbt->setShort(self::TAG_COOK_TIME, 0, true);
}
if(!$nbt->hasTag(self::TAG_MAX_TIME, ShortTag::class)){
$nbt->setTag(new ShortTag(self::TAG_MAX_TIME, $nbt->getShort(self::TAG_BURN_TIME)));
$nbt->setShort(self::TAG_MAX_TIME, $nbt->getShort(self::TAG_BURN_TIME), true);
$nbt->removeTag(self::TAG_BURN_TICKS);
}
if(!$nbt->getTag(self::TAG_BURN_TICKS, ShortTag::class)){
$nbt->setTag(new ShortTag(self::TAG_BURN_TICKS, 0));
$nbt->setShort(self::TAG_BURN_TICKS, 0, true);
}
parent::__construct($level, $nbt);

View File

@ -39,11 +39,11 @@ class ItemFrame extends Spawnable{
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_ITEM_ROTATION, ByteTag::class)){
$nbt->setTag(new ByteTag(self::TAG_ITEM_ROTATION, 0));
$nbt->setByte(self::TAG_ITEM_ROTATION, 0, true);
}
if(!$nbt->hasTag(self::TAG_ITEM_DROP_CHANCE, FloatTag::class)){
$nbt->setTag(new FloatTag(self::TAG_ITEM_DROP_CHANCE, 1.0));
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, 1.0, true);
}
parent::__construct($level, $nbt);

View File

@ -38,15 +38,17 @@ class Skull extends Spawnable{
const TYPE_CREEPER = 4;
const TYPE_DRAGON = 5;
const TAG_SKULL_TYPE = "SkullType";
const TAG_ROT = "Rot";
const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte
const TAG_ROT = "Rot"; //TAG_Byte
const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_SKULL_TYPE, ByteTag::class)){
$nbt->setTag(new ByteTag(self::TAG_SKULL_TYPE, 0));
$nbt->setByte(self::TAG_SKULL_TYPE, 0, true);
}
if(!$nbt->hasTag(self::TAG_ROT, ByteTag::class)){
$nbt->setTag(new ByteTag(self::TAG_ROT, 0));
$nbt->setByte(self::TAG_ROT, 0, true);
}
parent::__construct($level, $nbt);
}

View File

@ -164,8 +164,7 @@ class Binary{
/**
* Reads a 16-bit signed little-endian number
*
* @param $str
*
* @param string $str
* @return int
*/
public static function readSignedLShort(string $str) : int{
@ -175,8 +174,7 @@ class Binary{
/**
* Writes a 16-bit signed/unsigned little-endian number
*
* @param $value
*
* @param int $value
* @return string
*/
public static function writeLShort(int $value) : string{
@ -379,12 +377,11 @@ class Binary{
/**
* Reads an 8-byte integer.
*
* @param string $x
* @param string $str
* @return int
*/
public static function readLong(string $x) : int{
$int = unpack("N*", $x);
return ($int[1] << 32) | $int[2];
public static function readLong(string $str) : int{
return unpack("J", $str)[1];
}
/**
@ -394,7 +391,7 @@ class Binary{
* @return string
*/
public static function writeLong(int $value) : string{
return pack("NN", $value >> 32, $value & 0xFFFFFFFF);
return pack("J", $value);
}
/**
@ -404,7 +401,7 @@ class Binary{
* @return int
*/
public static function readLLong(string $str) : int{
return self::readLong(strrev($str));
return unpack("P", $str)[1];
}
/**
@ -414,7 +411,7 @@ class Binary{
* @return string
*/
public static function writeLLong(int $value) : string{
return strrev(self::writeLong($value));
return pack("P", $value);
}

View File

@ -342,7 +342,7 @@ class Config{
}
$base = $value;
$this->nestedCache[$key] = $value;
$this->nestedCache = [];
}
/**
@ -376,6 +376,26 @@ class Config{
return $this->nestedCache[$key] = $base;
}
public function removeNested(string $key) : void{
$this->nestedCache = [];
$vars = explode(".", $key);
$currentNode =& $this->config;
while(count($vars) > 0){
$nodeName = array_shift($vars);
if(isset($currentNode[$nodeName])){
if(empty($vars)){ //final node
unset($currentNode[$nodeName]);
}elseif(is_array($currentNode[$nodeName])){
$currentNode =& $currentNode[$nodeName];
}
}else{
break;
}
}
}
/**
* @param $k
* @param mixed $default

View File

@ -119,6 +119,10 @@ class MainLogger extends \AttachableThreadedLogger{
$this->logDebug = $logDebug;
}
/**
* @param \Throwable $e
* @param array|null $trace
*/
public function logException(\Throwable $e, $trace = null){
if($trace === null){
$trace = $e->getTrace();

@ -1 +1 @@
Subproject commit 9142e7decec23b302ab470a55e5ed160df4878db
Subproject commit a10881b0078559f646b570a12304dba71658357c

@ -1 +1 @@
Subproject commit 4e28d74c9aafdf51b10bc274ddcf78fcb3b317d4
Subproject commit 3446bcc774bcc278430e2cecd9d46a47f491656f