mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +00:00
Merge branch 'master' into mcpe-1.2.5
This commit is contained in:
commit
dcdea6a1f4
@ -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);
|
||||
|
@ -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")){
|
||||
|
@ -2156,6 +2156,10 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $e
|
||||
* @param array|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
if($e === null){
|
||||
return;
|
||||
|
@ -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(){
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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){
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -45,5 +45,6 @@ interface WindowTypes{
|
||||
const STRUCTURE_EDITOR = 14;
|
||||
const TRADING = 15;
|
||||
const COMMAND_BLOCK = 16;
|
||||
const JUKEBOX = 17;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user