diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index bfd9771e1..1c6feffe5 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -345,7 +345,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } public function isBanned(){ - return $this->server->getNameBans()->isBanned(strtolower($this->getName())); + return $this->server->getNameBans()->isBanned($this->iusername); } public function setBanned($value){ @@ -358,14 +358,14 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } public function isWhitelisted(){ - return $this->server->isWhitelisted(strtolower($this->getName())); + return $this->server->isWhitelisted($this->iusername); } public function setWhitelisted($value){ if($value === true){ - $this->server->addWhitelist(strtolower($this->getName())); + $this->server->addWhitelist($this->iusername); }else{ - $this->server->removeWhitelist(strtolower($this->getName())); + $this->server->removeWhitelist($this->iusername); } } @@ -1562,10 +1562,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - if(!$this->isSpectator()){ - $this->checkNearEntities($tickDiff); - } - $this->speed = ($to->subtract($from))->divide($tickDiff); }elseif($distanceSquared == 0){ $this->speed = new Vector3(0, 0, 0); @@ -1655,28 +1651,33 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->processMovement($tickDiff); $this->entityBaseTick($tickDiff); - if(!$this->isSpectator() and $this->speed !== null){ - if($this->onGround){ - if($this->inAirTicks !== 0){ - $this->startAirTicks = 5; - } - $this->inAirTicks = 0; - }else{ - if(!$this->allowFlight and $this->inAirTicks > 10 and !$this->isSleeping() and !$this->isImmobile()){ - $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - $this->startAirTicks)); - $diff = ($this->speed->y - $expectedVelocity) ** 2; + if(!$this->isSpectator()){ + $this->checkNearEntities($tickDiff); - if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ - if($this->inAirTicks < 100){ - $this->setMotion(new Vector3(0, $expectedVelocity, 0)); - }elseif($this->kick("Flying is not enabled on this server")){ - $this->timings->stopTiming(); - return false; + if($this->speed !== null){ + if($this->onGround){ + if($this->inAirTicks !== 0){ + $this->startAirTicks = 5; + } + $this->inAirTicks = 0; + }else{ + if(!$this->allowFlight and $this->inAirTicks > 10 and !$this->isSleeping() and !$this->isImmobile()){ + $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - $this->startAirTicks)); + $diff = ($this->speed->y - $expectedVelocity) ** 2; + + if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ + if($this->inAirTicks < 100){ + $this->setMotion(new Vector3(0, $expectedVelocity, 0)); + }elseif($this->kick("Flying is not enabled on this server")){ + $this->timings->stopTiming(); + + return false; + } } } - } - ++$this->inAirTicks; + ++$this->inAirTicks; + } } } } @@ -1722,18 +1723,18 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade protected function processLogin(){ - if(!$this->server->isWhitelisted(strtolower($this->getName()))){ + if(!$this->server->isWhitelisted($this->iusername)){ $this->close($this->getLeaveMessage(), "Server is white-listed"); return; - }elseif($this->server->getNameBans()->isBanned(strtolower($this->getName())) or $this->server->getIPBans()->isBanned($this->getAddress())){ + }elseif($this->server->getNameBans()->isBanned($this->iusername) or $this->server->getIPBans()->isBanned($this->getAddress())){ $this->close($this->getLeaveMessage(), "You are banned"); return; } foreach($this->server->getOnlinePlayers() as $p){ - if($p !== $this and strtolower($p->getName()) === strtolower($this->getName())){ + if($p !== $this and $p->iusername === $this->iusername){ if($p->kick("logged in from another location") === false){ $this->close($this->getLeaveMessage(), "Logged in from another location"); @@ -1975,7 +1976,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade //Client requested a resource pack but we don't have it available on the server $this->close("", "disconnectionScreen.resourcePack", true); $this->server->getLogger()->debug("Got a resource pack request for unknown pack with UUID " . $uuid . ", available packs: " . implode(", ", $manager->getPackIdList())); - break; + return false; } $pk = new ResourcePackDataInfoPacket(); @@ -2382,7 +2383,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; //TODO: handle these default: $this->server->getLogger()->debug("Unhandled/unknown interaction type " . $packet->action . "received from ". $this->getName()); - break; + return false; } return true; @@ -2721,7 +2722,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; //TODO default: $this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName()); - break; + return false; } $this->startAction = -1; @@ -3284,7 +3285,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->close("", "disconnectionScreen.resourcePack", true); $this->server->getLogger()->debug("Got a resource pack chunk request for unknown pack with UUID " . $packet->packId . ", available packs: " . implode(", ", $manager->getPackIdList())); - return true; + return false; } $pk = new ResourcePackChunkDataPacket(); @@ -3336,7 +3337,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } $this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet)); if(!$ev->isCancelled() and !$packet->handle($this)){ - $this->server->getLogger()->debug("Unhandled " . get_class($packet) . " received from " . $this->getName() . ": " . bin2hex($packet->buffer)); + $this->server->getLogger()->debug("Unhandled " . get_class($packet) . " received from " . $this->getName() . ": 0x" . bin2hex($packet->buffer)); } $timings->stopTiming(); @@ -3410,10 +3411,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade */ public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){ $this->setTitleDuration($fadeIn, $stay, $fadeOut); - $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); if($subtitle !== ""){ $this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE); } + $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); } /** diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 6c3724637..5699e1722 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -128,7 +128,6 @@ namespace pocketmine { set_time_limit(0); //Who set it to 30 seconds?!?! - gc_enable(); error_reporting(-1); ini_set("allow_url_fopen", 1); ini_set("display_errors", 1); diff --git a/src/pocketmine/block/Lava.php b/src/pocketmine/block/Lava.php index d90f34ad3..d0d07042b 100644 --- a/src/pocketmine/block/Lava.php +++ b/src/pocketmine/block/Lava.php @@ -64,7 +64,7 @@ class Lava extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/block/Liquid.php b/src/pocketmine/block/Liquid.php index 8c0f333b8..79e6730cd 100644 --- a/src/pocketmine/block/Liquid.php +++ b/src/pocketmine/block/Liquid.php @@ -189,7 +189,7 @@ abstract class Liquid extends Transparent{ public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $this->checkForHarden(); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); }elseif($type === Level::BLOCK_UPDATE_SCHEDULED){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); @@ -242,7 +242,7 @@ abstract class Liquid extends Transparent{ $this->getLevel()->setBlock($this, new Air(), true); }else{ $this->getLevel()->setBlock($this, Block::get($this->id, $decay), true); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); } }elseif($flag){ //$this->getLevel()->scheduleUpdate($this, $this->tickRate()); @@ -262,10 +262,10 @@ abstract class Liquid extends Transparent{ if($decay >= 8){ $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay), true); - $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate()); }else{ $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay + 8), true); - $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate()); } }elseif($decay >= 0 and ($decay === 0 or !$bottomBlock->canBeFlowedInto())){ $flags = $this->getOptimalFlowDirections(); @@ -310,7 +310,7 @@ abstract class Liquid extends Transparent{ } $this->getLevel()->setBlock($block, Block::get($this->getId(), $newFlowDecay), true); - $this->getLevel()->scheduleUpdate($block, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($block, $this->tickRate()); } } diff --git a/src/pocketmine/block/Water.php b/src/pocketmine/block/Water.php index 4e7da94a5..d71e4791a 100644 --- a/src/pocketmine/block/Water.php +++ b/src/pocketmine/block/Water.php @@ -48,7 +48,7 @@ class Water extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/entity/Effect.php b/src/pocketmine/entity/Effect.php index 49bd99525..a5327dbde 100644 --- a/src/pocketmine/entity/Effect.php +++ b/src/pocketmine/entity/Effect.php @@ -440,14 +440,10 @@ class Effect{ $attr->setMaxValue($max); break; case Effect::ABSORPTION: - if($ev->willModify() and $oldEffect !== null){ - $value = $entity->getAbsorption() - (4 * ($oldEffect->getAmplifier() + 1)); - }else{ - $value = $entity->getAbsorption(); + $new = (4 * ($this->amplifier + 1)); + if($new > $entity->getAbsorption()){ + $entity->setAbsorption($new); } - - $value += (4 * ($this->amplifier + 1)); - $entity->setAbsorption($value); break; } @@ -490,7 +486,7 @@ class Effect{ $attr->setMaxValue($attr->getMaxValue() - (4 * ($this->amplifier + 1))); break; case Effect::ABSORPTION: - $entity->setAbsorption($entity->getAbsorption() - (4 * ($this->amplifier + 1))); + $entity->setAbsorption(0); break; } } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index da4f5626b..e8b4e85aa 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -394,21 +394,38 @@ abstract class Entity extends Location implements Metadatable{ public function setNameTagAlwaysVisible($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value); } - + /** * @return float */ - public function getScale(): float{ + public function getScale() : float{ return $this->getDataProperty(self::DATA_SCALE); } - + /** * @param float $value */ public function setScale(float $value){ + $multiplier = $value / $this->getScale(); + + $this->width *= $multiplier; + $this->height *= $multiplier; + $halfWidth = $this->width / 2; + + $this->boundingBox->setBounds( + $this->x - $halfWidth, + $this->y, + $this->z - $halfWidth, + $this->x + $halfWidth, + $this->y + $this->height, + $this->z + $halfWidth + ); + $this->setDataProperty(self::DATA_SCALE, self::DATA_TYPE_FLOAT, $value); + $this->setDataProperty(self::DATA_BOUNDING_BOX_WIDTH, self::DATA_TYPE_FLOAT, $this->width); + $this->setDataProperty(self::DATA_BOUNDING_BOX_HEIGHT, self::DATA_TYPE_FLOAT, $this->height); } - + public function isSneaking(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING); } diff --git a/src/pocketmine/event/Event.php b/src/pocketmine/event/Event.php index b9fe376db..2b54763d0 100644 --- a/src/pocketmine/event/Event.php +++ b/src/pocketmine/event/Event.php @@ -30,8 +30,6 @@ abstract class Event{ * Any callable event must declare the static variable * * public static $handlerList = null; - * public static $eventPool = []; - * public static $nextEvent = 0; * * Not doing so will deny the proper event initialization */ @@ -85,4 +83,4 @@ abstract class Event{ return static::$handlerList; } -} \ No newline at end of file +} diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index 7e793d55e..7cc94878c 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -46,12 +46,22 @@ class Item implements ItemIds, \JsonSerializable{ private static $cachedParser = null; private static function parseCompoundTag(string $tag) : CompoundTag{ + if(strlen($tag) === 0){ + throw new \InvalidArgumentException("No NBT data found in supplied string"); + } + if(self::$cachedParser === null){ self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN); } self::$cachedParser->read($tag); - return self::$cachedParser->getData(); + $data = self::$cachedParser->getData(); + + if(!($data instanceof CompoundTag)){ + throw new \InvalidArgumentException("Invalid item NBT string given, it could not be deserialized"); + } + + return $data; } private static function writeCompoundTag(CompoundTag $tag) : string{ diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 5ece506e2..0fe5b3e6b 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -201,7 +201,7 @@ class Explosion{ $pos = new Vector3($block->x, $block->y, $block->z); - for($side = 0; $side < 5; $side++){ + for($side = 0; $side <= 5; $side++){ $sideBlock = $pos->getSide($side); if(!isset($this->affectedBlocks[$index = Level::blockHash($sideBlock->x, $sideBlock->y, $sideBlock->z)]) and !isset($updateBlocks[$index])){ $this->level->getServer()->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->level->getBlock($sideBlock))); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index b2ebe15b1..e10ac248a 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -190,8 +190,11 @@ class Level implements ChunkManager, Metadatable{ private $changedBlocks = []; /** @var ReversePriorityQueue */ - private $updateQueue; - private $updateQueueIndex = []; + private $scheduledBlockUpdateQueue; + private $scheduledBlockUpdateQueueIndex = []; + + /** @var \SplQueue */ + private $neighbourBlockUpdateQueue = []; /** @var Player[][] */ private $chunkSendQueue = []; @@ -333,8 +336,11 @@ class Level implements ChunkManager, Metadatable{ $this->generator = Generator::getGenerator($this->provider->getGenerator()); $this->folderName = $name; - $this->updateQueue = new ReversePriorityQueue(); - $this->updateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + $this->scheduledBlockUpdateQueue = new ReversePriorityQueue(); + $this->scheduledBlockUpdateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + + $this->neighbourBlockUpdateQueue = new \SplQueue(); + $this->time = (int) $this->provider->getTime(); $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4))); @@ -663,11 +669,24 @@ class Level implements ChunkManager, Metadatable{ //Do block updates $this->timings->doTickPending->startTiming(); - while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){ - $block = $this->getBlock($this->updateQueue->extract()["data"]); - unset($this->updateQueueIndex[Level::blockHash($block->x, $block->y, $block->z)]); + + //Delayed updates + while($this->scheduledBlockUpdateQueue->count() > 0 and $this->scheduledBlockUpdateQueue->current()["priority"] <= $currentTick){ + $block = $this->getBlock($this->scheduledBlockUpdateQueue->extract()["data"]); + unset($this->scheduledBlockUpdateQueueIndex[Level::blockHash($block->x, $block->y, $block->z)]); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); } + + //Normal updates + while($this->neighbourBlockUpdateQueue->count() > 0){ + $index = $this->neighbourBlockUpdateQueue->dequeue(); + Level::getBlockXYZ($index, $x, $y, $z); + $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($x, $y, $z)))); + if(!$ev->isCancelled()){ + $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); + } + } + $this->timings->doTickPending->stopTiming(); $this->timings->entityTick->startTiming(); @@ -1018,15 +1037,45 @@ class Level implements ChunkManager, Metadatable{ } /** + * @deprecated This method will be removed in the future due to misleading/ambiguous name. Use {@link Level#scheduleDelayedBlockUpdate} instead. + * * @param Vector3 $pos * @param int $delay */ public function scheduleUpdate(Vector3 $pos, int $delay){ - if(isset($this->updateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->updateQueueIndex[$index] <= $delay){ + $this->scheduleDelayedBlockUpdate($pos, $delay); + } + + /** + * Schedules a block update to be executed after the specified number of ticks. + * Blocks will be updated with the scheduled update type. + * + * @param Vector3 $pos + * @param int $delay + */ + public function scheduleDelayedBlockUpdate(Vector3 $pos, int $delay){ + if(isset($this->scheduledBlockUpdateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->scheduledBlockUpdateQueueIndex[$index] <= $delay){ return; } - $this->updateQueueIndex[$index] = $delay; - $this->updateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick()); + $this->scheduledBlockUpdateQueueIndex[$index] = $delay; + $this->scheduledBlockUpdateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick()); + } + + /** + * Schedules the blocks around the specified position to be updated at the end of this tick. + * Blocks will be updated with the normal update type. + * + * @param Vector3 $pos + */ + public function scheduleNeighbourBlockUpdates(Vector3 $pos){ + $pos = $pos->floor(); + + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x + 1, $pos->y, $pos->z)); + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x - 1, $pos->y, $pos->z)); + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y + 1, $pos->z)); + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y - 1, $pos->z)); + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y, $pos->z + 1)); + $this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y, $pos->z - 1)); } /** @@ -1440,9 +1489,8 @@ class Level implements ChunkManager, Metadatable{ $entity->scheduleUpdate(); } $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); + $this->scheduleNeighbourBlockUpdates($pos); } - - $this->updateAround($pos); } $this->timings->setBlock->stopTiming(); diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 52684896c..9f47296cf 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -307,7 +307,7 @@ class Chunk{ * @param int $level 0-15 */ public function setBlockSkyLight(int $x, int $y, int $z, int $level){ - if($this->getSubChunk($y >> 4)->setBlockSkyLight($x, $y & 0x0f, $z, $level)){ + if($this->getSubChunk($y >> 4, true)->setBlockSkyLight($x, $y & 0x0f, $z, $level)){ $this->hasChanged = true; } } @@ -334,7 +334,7 @@ class Chunk{ * @param int $level 0-15 */ public function setBlockLight(int $x, int $y, int $z, int $level){ - if($this->getSubChunk($y >> 4)->setBlockLight($x, $y & 0x0f, $z, $level)){ + if($this->getSubChunk($y >> 4, true)->setBlockLight($x, $y & 0x0f, $z, $level)){ $this->hasChanged = true; } } diff --git a/src/pocketmine/level/format/SubChunk.php b/src/pocketmine/level/format/SubChunk.php index 38cb57194..8a5cc5be6 100644 --- a/src/pocketmine/level/format/SubChunk.php +++ b/src/pocketmine/level/format/SubChunk.php @@ -47,8 +47,11 @@ class SubChunk{ } public function isEmpty() : bool{ - assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids)); - return substr_count($this->ids, "\x00") === 4096; + return ( + substr_count($this->ids, "\x00") === 4096 and + substr_count($this->skyLight, "\xff") === 2048 and + substr_count($this->blockLight, "\x00") === 2048 + ); } public function getBlockId(int $x, int $y, int $z) : int{ diff --git a/src/pocketmine/nbt/tag/CompoundTag.php b/src/pocketmine/nbt/tag/CompoundTag.php index 99f384a4e..45ab5bf6d 100644 --- a/src/pocketmine/nbt/tag/CompoundTag.php +++ b/src/pocketmine/nbt/tag/CompoundTag.php @@ -111,4 +111,12 @@ class CompoundTag extends NamedTag implements \ArrayAccess{ } return $str . "}"; } + + public function __clone(){ + foreach($this as $key => $tag){ + if($tag instanceof Tag){ + $this->{$key} = clone $tag; + } + } + } } \ No newline at end of file diff --git a/src/pocketmine/nbt/tag/ListTag.php b/src/pocketmine/nbt/tag/ListTag.php index 3fed630be..361db14d6 100644 --- a/src/pocketmine/nbt/tag/ListTag.php +++ b/src/pocketmine/nbt/tag/ListTag.php @@ -220,4 +220,12 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{ } return $str . "}"; } + + public function __clone(){ + foreach($this as $key => $tag){ + if($tag instanceof Tag){ + $this->{$key} = clone $tag; + } + } + } } diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php index 758c47077..0e756fbc8 100644 --- a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php +++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php @@ -32,8 +32,8 @@ interface ProtocolInfo{ * Actual Minecraft: PE protocol version */ const CURRENT_PROTOCOL = 110; - const MINECRAFT_VERSION = "v1.1.0.0 beta"; - const MINECRAFT_VERSION_NETWORK = "1.1.0.0"; + const MINECRAFT_VERSION = 'v1.1.0.3 beta'; + const MINECRAFT_VERSION_NETWORK = '1.1.0.3'; const LOGIN_PACKET = 0x01; const PLAY_STATUS_PACKET = 0x02; @@ -41,7 +41,7 @@ interface ProtocolInfo{ const CLIENT_TO_SERVER_HANDSHAKE_PACKET = 0x04; const DISCONNECT_PACKET = 0x05; const RESOURCE_PACKS_INFO_PACKET = 0x06; - const RESOURCE_PACK_STACK_PACKET = 0x07; //ResourcePacksStackPacket + const RESOURCE_PACK_STACK_PACKET = 0x07; const RESOURCE_PACK_CLIENT_RESPONSE_PACKET = 0x08; const TEXT_PACKET = 0x09; const SET_TIME_PACKET = 0x0a; @@ -71,7 +71,7 @@ interface ProtocolInfo{ const BLOCK_PICK_REQUEST_PACKET = 0x22; const USE_ITEM_PACKET = 0x23; const PLAYER_ACTION_PACKET = 0x24; - const ENTITY_FALL_PACKET = 0x25; //PlayerFallPacket + const ENTITY_FALL_PACKET = 0x25; const HURT_ARMOR_PACKET = 0x26; const SET_ENTITY_DATA_PACKET = 0x27; const SET_ENTITY_MOTION_PACKET = 0x28; @@ -99,14 +99,14 @@ interface ProtocolInfo{ const SET_PLAYER_GAME_TYPE_PACKET = 0x3e; const PLAYER_LIST_PACKET = 0x3f; const SIMPLE_EVENT_PACKET = 0x40; - const EVENT_PACKET = 0x41; //TelemetryEventPacket + const EVENT_PACKET = 0x41; const SPAWN_EXPERIENCE_ORB_PACKET = 0x42; - const CLIENTBOUND_MAP_ITEM_DATA_PACKET = 0x43; //MapItemDataPacket + const CLIENTBOUND_MAP_ITEM_DATA_PACKET = 0x43; const MAP_INFO_REQUEST_PACKET = 0x44; const REQUEST_CHUNK_RADIUS_PACKET = 0x45; const CHUNK_RADIUS_UPDATED_PACKET = 0x46; const ITEM_FRAME_DROP_ITEM_PACKET = 0x47; - const REPLACE_ITEM_IN_SLOT_PACKET = 0x48; //ReplaceSelectedItemPacket + const REPLACE_ITEM_IN_SLOT_PACKET = 0x48; const GAME_RULES_CHANGED_PACKET = 0x49; const CAMERA_PACKET = 0x4a; const ADD_ITEM_PACKET = 0x4b; @@ -126,5 +126,7 @@ interface ProtocolInfo{ const SET_TITLE_PACKET = 0x59; const ADD_BEHAVIOR_TREE_PACKET = 0x5a; const STRUCTURE_BLOCK_UPDATE_PACKET = 0x5b; + const SHOW_STORE_OFFER_PACKET = 0x5c; + const PURCHASE_RECEIPT_PACKET = 0x5d; } diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php index 8b077933a..6162056ae 100644 --- a/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ResourcePacksInfoPacket.php @@ -39,20 +39,22 @@ class ResourcePacksInfoPacket extends DataPacket{ public function decode(){ /*$this->mustAccept = $this->getBool(); - $behaviorPackCount = $this->getUnsignedVarInt(); + $behaviorPackCount = $this->getLShort(); while($behaviorPackCount-- > 0){ $id = $this->getString(); $version = $this->getString(); - $size = $this->getUnsignedVarLong(); + $size = $this->getLLong(); $this->behaviorPackEntries[] = new ResourcePackInfoEntry($id, $version, $size); + $this->getString(); } - $resourcePackCount = $this->getUnsignedVarInt(); + $resourcePackCount = $this->getLShort(); while($resourcePackCount-- > 0){ $id = $this->getString(); $version = $this->getString(); - $size = $this->getUnsignedVarLong(); + $size = $this->getLLong(); $this->resourcePackEntries[] = new ResourcePackInfoEntry($id, $version, $size); + $this->getString(); }*/ } @@ -60,17 +62,19 @@ class ResourcePacksInfoPacket extends DataPacket{ $this->reset(); $this->putBool($this->mustAccept); - $this->putUnsignedVarInt(count($this->behaviorPackEntries)); + $this->putLShort(count($this->behaviorPackEntries)); foreach($this->behaviorPackEntries as $entry){ $this->putString($entry->getPackId()); $this->putString($entry->getPackVersion()); - $this->putUnsignedVarLong($entry->getPackSize()); + $this->putLLong($entry->getPackSize()); + $this->putString(""); //TODO } - $this->putUnsignedVarInt(count($this->resourcePackEntries)); + $this->putLShort(count($this->resourcePackEntries)); foreach($this->resourcePackEntries as $entry){ $this->putString($entry->getPackId()); $this->putString($entry->getPackVersion()); - $this->putUnsignedVarLong($entry->getPackSize()); + $this->putLLong($entry->getPackSize()); + $this->putString(""); //TODO } } diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 132890293..8efe10809 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -183,7 +183,7 @@ auto-updater: timings: #Choose the host to use for viewing your timings results. - host: mcpetimings.com + host: timings.pmmp.io console: #Choose whether to enable server stats reporting on the console title. diff --git a/src/pocketmine/utils/Config.php b/src/pocketmine/utils/Config.php index 24547d989..9b9a2211f 100644 --- a/src/pocketmine/utils/Config.php +++ b/src/pocketmine/utils/Config.php @@ -50,6 +50,8 @@ class Config{ private $correct = false; /** @var int */ private $type = Config::DETECT; + /** @var int */ + private $jsonOptions = JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING; public static $formats = [ "properties" => Config::PROPERTIES, @@ -187,7 +189,7 @@ class Config{ $content = $this->writeProperties(); break; case Config::JSON: - $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING); + $content = json_encode($this->config, $this->jsonOptions); break; case Config::YAML: $content = yaml_emit($this->config, YAML_UTF8_ENCODING); @@ -219,6 +221,68 @@ class Config{ } } + /** + * Sets the options for the JSON encoding when saving + * + * @param int $options + * @return Config $this + * @throws \RuntimeException if the Config is not in JSON + * @see json_encode + */ + public function setJsonOptions(int $options) : Config{ + if($this->type !== Config::JSON){ + throw new \RuntimeException("Attempt to set JSON options for non-JSON config"); + } + $this->jsonOptions = $options; + return $this; + } + + /** + * Enables the given option in addition to the currently set JSON options + * + * @param int $option + * @return Config $this + * @throws \RuntimeException if the Config is not in JSON + * @see json_encode + */ + public function enableJsonOption(int $option) : Config{ + if($this->type !== Config::JSON){ + throw new \RuntimeException("Attempt to enable JSON option for non-JSON config"); + } + $this->jsonOptions |= $option; + return $this; + } + + /** + * Disables the given option for the JSON encoding when saving + * + * @param int $option + * @return Config $this + * @throws \RuntimeException if the Config is not in JSON + * @see json_encode + */ + public function disableJsonOption(int $option) : Config{ + if($this->type !== Config::JSON){ + throw new \RuntimeException("Attempt to disable JSON option for non-JSON config"); + } + $this->jsonOptions &= ~$option; + return $this; + } + + /** + * Returns the options for the JSON encoding when saving + * + * @return int + * @throws \RuntimeException if the Config is not in JSON + * @see json_encode + */ + public function getJsonOptions() : int{ + if($this->type !== Config::JSON){ + throw new \RuntimeException("Attempt to get JSON options for non-JSON config"); + } + return $this->jsonOptions; + } + /** * @param $k *