Compare commits

..

30 Commits
3.0.6 ... 3.0.9

Author SHA1 Message Date
0d9f40873f disable dev flag 2018-07-30 14:57:51 +01:00
a4aee98cba TimingsCommand: some code cleanup 2018-07-30 14:53:10 +01:00
a97c7d3132 Fix for timings 2018-07-30 14:42:16 +01:00
4a1ed21e52 PluginManager: Fix patch level check to allow loading the plugin when the server's minor level is higher than the plugins declared minor level. 2018-07-27 11:46:24 +01:00
1b053c7928 Clean up pointless checks in Thread/Worker 2018-07-26 14:20:55 +01:00
c684f99cc4 Clean up Thread/Worker quit() 2018-07-26 14:17:01 +01:00
695793795e PluginManager: Remove dead $pluginParentTimer left over from 9e4d88a852 2018-07-26 10:25:01 +01:00
a4965842d6 Remove $handlerList from PlayerExperienceChangeEvent 2018-07-25 15:30:01 +01:00
1ef6f5d166 ZippedResourcePack: Make manifest parse errors less useless 2018-07-21 09:53:16 +01:00
eccc249009 KillCommand: clean up old shitcode 2018-07-20 19:44:41 +01:00
dbaf7287bc back to dev 2018-07-20 12:20:24 +01:00
b3b240e25b disable dev flag 2018-07-20 12:05:14 +01:00
2b30ef1671 Revert "Living: fix knockback condition, take 2"
This reverts commit 0081e30a89.

The logic introduced by this commit is correct in MC JAVA 1.9+. Unfortunately, nobody likes 1.9+ for combat.
Some testing in MCPE vanilla made it apparent that this logic isn't correct for MCPE. The old logic is correct for pre-1.9 knockback.
2018-07-20 11:55:10 +01:00
124ebf69c5 PlayStatusPacket: default to current protocol if not specified 2018-07-20 11:29:40 +01:00
4274640845 Player: fixed on-ground state not being updated when walking horizontally
it's possible to walk off a tower while flying without moving vertically, and this code previously wouldn't detect that, leaving a gaping hole in the anti-cheat.
2018-07-18 15:14:18 +01:00
58b665985e back to dev 2018-07-17 18:09:24 +01:00
0f5c48e342 Disable dev flag for release 2018-07-17 16:59:00 +01:00
08ad5db05b Config: remove useless switch cases
CNF is the same type as PROPERTIES (it's an alias) so these cases are useless.
2018-07-17 16:56:47 +01:00
94e8623c75 Server: account for default provider being missing 2018-07-17 12:14:26 +01:00
921f7e8f6a Level: remove useless check from populateChunk()
this is already checked at the top of the function.
2018-07-16 17:36:36 +01:00
710e1d014d Entity: fixed 0-length motion vectors being passed to move()
this was an interesting bug.

This was discovered by making a projectile's drag 0, making its gravity a factor of its throw force (such that force / gravity = integer value), and then throwing it directly up. At the apex, an error would occur due to trying to do a ray trace with a zero vector.

This also led me to realize that there's an edge case in the current movement system - if an entity's motion reaches 0, it will stop getting movement updates. This can be undesirable when things such as gravity cause motion to become zero when throwing a projectile directly upwards. This will need to be fixed separately.
2018-07-16 12:08:13 +01:00
7fc22d3227 Entity: fixed setNameTagAlwaysVisible()
mojang >.<

this doesn't fix the problem of invisibility making nametags hidden though.
2018-07-14 16:05:46 +01:00
7bfe487ee5 ConcretePowder: fixed a missed usage of Block::get() 2018-07-14 10:35:05 +01:00
d8cf835f92 BlockFactory: better handling for dodgy IDs
I thought I'd already dealt with this, but it seems not.
2018-07-13 12:31:22 +01:00
65e44364e5 Added some debug for raw packets and Query handling 2018-07-13 10:07:11 +01:00
ebbbc581ca Player: clean up cursor inventory when closing main inventory 2018-07-12 17:52:22 +01:00
8aa8280a63 Level: Make spawn protection always active regardless of op count (#2290)
I don't care if this matches PC behaviour or not. bugs.mojang.com is full of bug reports about this. Just search for "minecraft spawn protection not working" and you'll see what I mean.

If you want to disable spawn protection, actually disable it. This behaviour is something that most users are not aware of and find astonishing when they discover it.

This behaviour was copied from Minecraft PC, and it's nearly as unexpected there as it is here.

This commit reverses the stupidity done in eb0525e892.
2018-07-12 17:25:05 +01:00
6a637d9099 update pthreads version for travis 2018-07-12 17:23:52 +01:00
06b80a9536 Level: Make getSafeSpawn() account for non-generated chunks
fixes #2295

There is still an issue in that the spawn point will not be offset if the chunk is not generated, but this is better than the spawn point being down at y=0. The other issue is a job for another time.
2018-07-11 10:17:59 +01:00
b3ffce9729 back to dev 2018-07-11 09:14:38 +01:00
20 changed files with 113 additions and 141 deletions

View File

@ -6,9 +6,9 @@ php:
before_script:
# - pecl install channel://pecl.php.net/pthreads-3.1.6
- echo | pecl install channel://pecl.php.net/yaml-2.0.2
- git clone https://github.com/krakjoe/pthreads.git
- git clone https://github.com/pmmp/pthreads.git
- cd pthreads
- git checkout d32079fb4a88e6e008104d36dbbf0c2dd7deb403
- git checkout c8cfacda84f21032d6014b53e72bf345ac901dac
- phpize
- ./configure
- make

View File

@ -1487,14 +1487,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
if(!$this->onGround or $movY != 0){
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
$bb = clone $this->boundingBox;
$bb->minY = $this->y - 0.2;
$bb->maxY = $this->y + 0.2;
$this->onGround = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
$this->isCollided = $this->onGround;
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
}
public function canBeMovedByCurrents() : bool{
@ -2168,7 +2165,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return false;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$message = TextFormat::clean($message, $this->removeFormat);
foreach(explode("\n", $message) as $messagePart){
@ -2243,7 +2240,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
switch($packet->event){
case EntityEventPacket::EATING_ITEM:
@ -2394,7 +2391,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
$this->resetCraftingGridType();
$this->doCloseInventory();
$item = $this->inventory->getItemInHand();
$oldItem = clone $item;
@ -2635,7 +2632,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$target = $this->level->getEntity($packet->target);
if($target === null){
@ -2849,7 +2846,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
if(isset($this->windowIndex[$packet->windowId])){
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
@ -2899,7 +2896,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!$this->spawned or !$this->isAlive()){
return true;
}
$this->resetCraftingGridType();
$this->doCloseInventory();
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this) > 10000 or $this->level->checkSpawnProtection($this, $pos)){
@ -3617,7 +3614,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//Crafting grid must always be evacuated even if keep-inventory is true. This dumps the contents into the
//main inventory and drops the rest on the ground.
$this->resetCraftingGridType();
$this->doCloseInventory();
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)));
@ -3800,15 +3797,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->craftingGrid = $grid;
}
public function resetCraftingGridType() : void{
$contents = $this->craftingGrid->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
public function doCloseInventory() : void{
/** @var Inventory[] $inventories */
$inventories = [$this->craftingGrid, $this->cursorInventory];
foreach($inventories as $inventory){
$contents = $inventory->getContents();
if(count($contents) > 0){
$drops = $this->inventory->addItem(...$contents);
foreach($drops as $drop){
$this->dropItem($drop);
}
$this->craftingGrid->clearAll();
$inventory->clearAll();
}
}
if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){

View File

@ -37,7 +37,7 @@ namespace pocketmine {
use pocketmine\wizard\SetupWizard;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.0.6";
const BASE_VERSION = "3.0.9";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;

View File

@ -1070,6 +1070,9 @@ class Server{
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
if($providerClass === null){
throw new \InvalidStateException("Default level provider has not been registered");
}
}
try{
@ -2471,6 +2474,8 @@ class Server{
try{
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
$this->queryHandler->handle($interface, $address, $port, $payload);
}else{
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
}
}catch(\Throwable $e){
if(\pocketmine\DEBUG > 1){

View File

@ -67,14 +67,10 @@ abstract class Thread extends \Thread{
public function start(?int $options = \PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return parent::start($options);
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return false;
return parent::start($options);
}
/**
@ -83,12 +79,9 @@ abstract class Thread extends \Thread{
public function quit(){
$this->isKilled = true;
$this->notify();
if(!$this->isJoined()){
if(!$this->isTerminated()){
$this->join();
}
$this->notify();
$this->join();
}
ThreadManager::getInstance()->remove($this);

View File

@ -67,14 +67,10 @@ abstract class Worker extends \Worker{
public function start(?int $options = \PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return parent::start($options);
if($this->getClassLoader() === null){
$this->setClassLoader();
}
return false;
return parent::start($options);
}
/**
@ -83,16 +79,10 @@ abstract class Worker extends \Worker{
public function quit(){
$this->isKilled = true;
$this->notify();
if($this->isRunning()){
$this->shutdown();
while($this->unstack() !== null);
$this->notify();
$this->unstack();
}elseif(!$this->isJoined()){
if(!$this->isTerminated()){
$this->join();
}
$this->shutdown();
}
ThreadManager::getInstance()->remove($this);

View File

@ -25,7 +25,6 @@ namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\level\Position;
use pocketmine\utils\MainLogger;
/**
* Manages block registration and instance creation
@ -436,19 +435,12 @@ class BlockFactory{
* @return int
*/
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
if($id === Block::AIR){
//TODO: HACK! (weird air blocks with non-zero damage values shouldn't turn into update! blocks)
$meta = 0;
}
$index = ($id << 4) | $meta;
if(!isset(self::$staticRuntimeIdMap[$index])){
self::registerMapping($rtId = ++self::$lastRuntimeId, $id, $meta);
MainLogger::getLogger()->error("ID $id meta $meta does not have a corresponding block static runtime ID, added a new unknown runtime ID ($rtId)");
return $rtId;
}
return self::$staticRuntimeIdMap[$index];
/*
* try id+meta first
* if not found, try id+0 (strip meta)
* if still not found, return update! block
*/
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
}
/**

View File

@ -66,7 +66,7 @@ class ConcretePowder extends Fallable{
private function checkAdjacentWater() : ?Block{
for($i = 1; $i < 6; ++$i){ //Do not check underneath
if($this->getSide($i) instanceof Water){
return Block::get(Block::CONCRETE, $this->meta);
return BlockFactory::get(Block::CONCRETE, $this->meta);
}
}

View File

@ -62,15 +62,7 @@ class KillCommand extends VanillaCommand{
$player = $sender->getServer()->getPlayer($args[0]);
if($player instanceof Player){
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
if($ev->isCancelled()){
return true;
}
$player->setLastDamageCause($ev);
$player->setHealth(0);
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kill.successful", [$player->getName()]));
}else{
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
@ -86,14 +78,7 @@ class KillCommand extends VanillaCommand{
return true;
}
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
if($ev->isCancelled()){
return true;
}
$sender->setLastDamageCause($ev);
$sender->setHealth(0);
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
$sender->sendMessage(new TranslationContainer("commands.kill.successful", [$sender->getName()]));
}else{
throw new InvalidCommandSyntaxException();

View File

@ -98,21 +98,33 @@ class TimingsCommand extends VanillaCommand{
if($paste){
fseek($fileTimings, 0);
$data = [
"syntax" => "text",
"poster" => $sender->getServer()->getName(),
"content" => stream_get_contents($fileTimings)
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
"data" => $content = stream_get_contents($fileTimings)
];
fclose($fileTimings);
$sender->getServer()->getAsyncPool()->submitTask(new class([
["page" => "http://paste.ubuntu.com", "extraOpts" => [
CURLOPT_HTTPHEADER => ["User-Agent: " . $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion()],
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $data,
CURLOPT_AUTOREFERER => false,
CURLOPT_FOLLOWLOCATION => false
]]
], $sender) extends BulkCurlTask{
$host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io");
$sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{
/** @var string */
private $host;
public function __construct(CommandSender $sender, string $host, string $agent, array $data){
parent::__construct([
["page" => "https://$host?upload=true", "extraOpts" => [
CURLOPT_HTTPHEADER => [
"User-Agent: $agent",
"Content-Type: application/x-www-form-urlencoded"
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_AUTOREFERER => false,
CURLOPT_FOLLOWLOCATION => false
]]
], $sender);
$this->host = $host;
}
public function onCompletion(Server $server){
$sender = $this->fetchLocal();
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
@ -123,23 +135,14 @@ class TimingsCommand extends VanillaCommand{
$server->getLogger()->logException($result);
return;
}
list(, $headers) = $result;
foreach($headers as $headerGroup){
if(isset($headerGroup["location"]) and preg_match('#^http://paste\\.ubuntu\\.com/([A-Za-z0-9+\/=]+)/#', trim($headerGroup["location"]), $match)){
$pasteId = $match[1];
break;
}
}
if(isset($pasteId)){
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsUpload", ["http://paste.ubuntu.com/" . $pasteId . "/"]));
if(isset($result[0]) && is_array($response = json_decode($result[0], true)) && isset($response["id"])){
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead",
["http://" . $sender->getServer()->getProperty("timings.host", "timings.pmmp.io") . "/?url=" . urlencode($pasteId)]));
["https://" . $this->host . "/?id=" . $response["id"]]));
}else{
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError"));
}
}
});
}else{
fclose($fileTimings);
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));

View File

@ -169,7 +169,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_LIMITED_LIFE = 77;
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
/* 80 (byte) something to do with nametag visibility? */
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
public const DATA_COLOR_2 = 81; //byte
/* 82 (unknown) */
public const DATA_SCORE_TAG = 83; //string
@ -606,7 +606,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
* @param bool $value
*/
public function setNameTagAlwaysVisible(bool $value = true) : void{
$this->setGenericFlag(self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value);
$this->propertyManager->setByte(self::DATA_ALWAYS_SHOW_NAMETAG, $value ? 1 : 0);
}
/**
@ -1346,7 +1346,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
if($this->hasMovementUpdate()){
$this->tryChangeMovement();
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
if(abs($this->motion->x) <= self::MOTION_THRESHOLD){
$this->motion->x = 0;
@ -1358,6 +1357,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$this->motion->z = 0;
}
if($this->motion->x != 0 or $this->motion->y != 0 or $this->motion->z != 0){
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
}
$this->forceMovementUpdate = false;
}

View File

@ -575,17 +575,14 @@ abstract class Living extends Entity implements Damageable{
$motion = clone $this->motion;
$motion->x /= 2;
$motion->y /= 2;
$motion->z /= 2;
$motion->x += $x * $f * $base;
$motion->y += $base;
$motion->z += $z * $f * $base;
if($this->onGround){
$motion->y /= 2;
$motion->y += $base;
if($motion->y > 0.4){ //this is hardcoded in vanilla
$motion->y = 0.4;
}
if($motion->y > $base){
$motion->y = $base;
}
$this->setMotion($motion);

View File

@ -31,7 +31,6 @@ use pocketmine\event\entity\EntityEvent;
* Called when a player gains or loses XP levels and/or progress.
*/
class PlayerExperienceChangeEvent extends EntityEvent implements Cancellable{
public static $handlerList = null;
/** @var Human */
protected $entity;
/** @var int */

View File

@ -1627,7 +1627,7 @@ class Level implements ChunkManager, Metadatable{
$spawnLocation = $this->getSpawnLocation();
$s = new Vector2($spawnLocation->x, $spawnLocation->z);
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){
if($t->distance($s) <= $distance){
return true;
}
}
@ -2791,7 +2791,7 @@ class Level implements ChunkManager, Metadatable{
$chunk = $this->getChunk($v->x >> 4, $v->z >> 4, false);
$x = (int) $v->x;
$z = (int) $v->z;
if($chunk !== null){
if($chunk !== null and $chunk->isGenerated()){
$y = (int) min($max - 2, $v->y);
$wasAir = ($chunk->getBlockId($x & 0x0f, $y - 1, $z & 0x0f) === 0);
for(; $y > 0; --$y){
@ -2947,16 +2947,14 @@ class Level implements ChunkManager, Metadatable{
}
if($populate){
if(!isset($this->chunkPopulationQueue[$index])){
$this->chunkPopulationQueue[$index] = true;
for($xx = -1; $xx <= 1; ++$xx){
for($zz = -1; $zz <= 1; ++$zz){
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
}
$this->chunkPopulationQueue[$index] = true;
for($xx = -1; $xx <= 1; ++$xx){
for($zz = -1; $zz <= 1; ++$zz){
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
}
$task = new PopulationTask($this, $chunk);
$this->server->getAsyncPool()->submitTask($task);
}
$task = new PopulationTask($this, $chunk);
$this->server->getAsyncPool()->submitTask($task);
}
Timings::$populationTimer->stopTiming();

View File

@ -47,7 +47,7 @@ class PlayStatusPacket extends DataPacket{
* @var int
* Used to determine how to write the packet when we disconnect incompatible clients.
*/
public $protocol;
public $protocol = ProtocolInfo::CURRENT_PROTOCOL;
protected function decodePayload(){
$this->status = $this->getInt();

View File

@ -58,6 +58,11 @@ class QueryHandler{
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.server.query.running", [$addr, $port]));
}
private function debug(string $message) : void{
//TODO: replace this with a proper prefixed logger
$this->server->getLogger()->debug("[Query] $message");
}
public function regenerateInfo(){
$ev = $this->server->getQueryInformation();
$this->longData = $ev->getLongQuery();
@ -91,7 +96,8 @@ class QueryHandler{
break;
case self::STATISTICS: //Stat
$token = Binary::readInt(substr($payload, 0, 4));
if($token !== self::getTokenString($this->token, $address) and $token !== self::getTokenString($this->lastToken, $address)){
if($token !== ($t1 = self::getTokenString($this->token, $address)) and $token !== ($t2 = self::getTokenString($this->lastToken, $address))){
$this->debug("Bad token $token from $address $port, expected $t1 or $t2");
break;
}
$reply = chr(self::STATISTICS);
@ -108,6 +114,9 @@ class QueryHandler{
}
$interface->sendRawPacket($address, $port, $reply);
break;
default:
$this->debug("Unhandled packet from $address $port: 0x" . bin2hex($packet));
break;
}
}
}

View File

@ -102,9 +102,6 @@ class PluginManager{
/** @var string|null */
private $pluginDataDirectory;
/** @var TimingsHandler */
public static $pluginParentTimer;
/**
* @param Server $server
* @param SimpleCommandMap $commandMap
@ -416,7 +413,7 @@ class PluginManager{
continue;
}
if($pluginNumbers[2] > $serverNumbers[2]){ //If the plugin requires bug fixes in patches, being backwards compatible
if($pluginNumbers[1] === $serverNumbers[1] and $pluginNumbers[2] > $serverNumbers[2]){ //If the plugin requires bug fixes in patches, being backwards compatible
continue;
}
}
@ -844,7 +841,7 @@ class PluginManager{
throw new PluginException("Plugin attempted to register " . $event . " while not enabled");
}
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")", self::$pluginParentTimer);
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")");
$this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled, $timings));
}

View File

@ -86,8 +86,11 @@ class ZippedResourcePack implements ResourcePack{
$archive->close();
$manifest = json_decode($manifestData);
if($manifest === null or !self::verifyManifest($manifest)){
throw new ResourcePackException("manifest.json is invalid or incomplete");
if($manifest === null){
throw new ResourcePackException("Failed to parse manifest.json: " . json_last_error_msg());
}
if(!self::verifyManifest($manifest)){
throw new ResourcePackException("manifest.json is missing required fields");
}
$this->manifest = $manifest;

View File

@ -26,7 +26,6 @@ namespace pocketmine\timings;
use pocketmine\entity\Entity;
use pocketmine\network\mcpe\protocol\DataPacket;
use pocketmine\Player;
use pocketmine\plugin\PluginManager;
use pocketmine\scheduler\TaskHandler;
use pocketmine\tile\Tile;
@ -134,7 +133,7 @@ abstract class Timings{
self::$timerEntityBaseTick = new TimingsHandler("** entityBaseTick");
self::$timerLivingEntityBaseTick = new TimingsHandler("** livingEntityBaseTick");
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer);
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks");
self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks");
self::$playerCommandTimer = new TimingsHandler("** playerCommand");

View File

@ -144,7 +144,6 @@ class Config{
$content = file_get_contents($this->file);
switch($this->type){
case Config::PROPERTIES:
case Config::CNF:
$this->parseProperties($content);
break;
case Config::JSON:
@ -197,7 +196,6 @@ class Config{
$content = null;
switch($this->type){
case Config::PROPERTIES:
case Config::CNF:
$content = $this->writeProperties();
break;
case Config::JSON: