diff --git a/.travis.yml b/.travis.yml index 2525a1f80..dcc2e1298 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_script: - echo | pecl install channel://pecl.php.net/yaml-2.0.4 - git clone https://github.com/pmmp/pthreads.git - cd pthreads - - git checkout 6ca019c58b4fa09ee2ff490f2444e34bef0773d0 + - git checkout 1b7da492b944146fa9680f6399bd9c6c6c6095e0 - phpize - ./configure - make diff --git a/README.md b/README.md index 5b4e0fcbc..ec613ee56 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ ## Donate - Bitcoin Cash (BCH): `qq3r46hn6ljnhnqnfwxt5pg3g447eq9jhvw5ddfear` - Bitcoin (BTC): `171u8K9e4FtU6j3e5sqNoxKUgEw9qWQdRV` +- Stellar Lumens (XLM): `GAAC5WZ33HCTE3BFJFZJXONMEIBNHFLBXM2HJVAZHXXPYA3HP5XPPS7T` - [Patreon](https://www.patreon.com/pocketminemp) ## Licensing information diff --git a/build/make-release.php b/build/make-release.php index f23a036e1..c8a475e57 100644 --- a/build/make-release.php +++ b/build/make-release.php @@ -76,6 +76,6 @@ system('git tag ' . $currentVer->getBaseVersion()); replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true); system('git add "' . $versionInfoPath . '"'); system('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"'); -echo "pushing changes in 10 seconds\n"; -sleep(10); +echo "pushing changes in 5 seconds\n"; +sleep(5); system('git push origin HEAD ' . $currentVer->getBaseVersion()); diff --git a/build/php b/build/php index 1b3fe3120..185d74199 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 1b3fe3120cac93ef8d821a1c62a3722576fcfd81 +Subproject commit 185d7419914005530298bd5e069449bdf4c0be56 diff --git a/changelogs/3.9.md b/changelogs/3.9.md index 0618aef71..57c82b7cd 100644 --- a/changelogs/3.9.md +++ b/changelogs/3.9.md @@ -99,4 +99,9 @@ Plugin developers should **only** update their required API to this version if y - `pocketmine\level\Explosion`: - `explodeA()` - `explodeB()` -- Fixed various cosmetic documentation inconsistencies in the core and dependencies. \ No newline at end of file +- Fixed various cosmetic documentation inconsistencies in the core and dependencies. + +# 3.9.7 +- Fixed a crash that could occur during timezone detection. +- Squid no longer spin around constantly in enclosed spaces. Their performance impact is reduced. +- Cleaned up the bootstrap file. \ No newline at end of file diff --git a/composer.json b/composer.json index cce152633..d9216be1a 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,11 @@ "autoload": { "psr-4": { "": ["src"] - } + }, + "files": [ + "src/pocketmine/GlobalConstants.php", + "src/pocketmine/VersionInfo.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..eeae576fe --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,124 @@ + + +parameters: + level: 1 + autoload_files: + - tests/phpstan/bootstrap.php + - src/pocketmine/PocketMine.php + paths: + - src + reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings + ignoreErrors: + - + message: "#^pocketmine\\\\Player\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\entity\\\\Human\\.$#" + path: src/pocketmine/Player.php + + - + message: "#^pocketmine\\\\block\\\\[A-Za-z\\d]+\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\block\\\\Block\\.$#" + path: src/pocketmine/block + + - + message: "#^pocketmine\\\\block\\\\Block\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\level\\\\Position\\.$#" + count: 1 + path: src/pocketmine/block/Block.php + + - + message: "#^pocketmine\\\\inventory\\\\DoubleChestInventory\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\inventory\\\\ChestInventory\\.$#" + count: 1 + path: src/pocketmine/inventory/DoubleChestInventory.php + + - + message: "#^pocketmine\\\\inventory\\\\EnderChestInventory\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\inventory\\\\ChestInventory\\.$#" + count: 1 + path: src/pocketmine/inventory/EnderChestInventory.php + + - + message: "#^pocketmine\\\\item\\\\GoldenAppleEnchanted\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\item\\\\GoldenApple\\.$#" + count: 1 + path: src/pocketmine/item/GoldenAppleEnchanted.php + + - + message: "#^pocketmine\\\\item\\\\WrittenBook\\:\\:__construct\\(\\) does not call parent constructor from pocketmine\\\\item\\\\WritableBook\\.$#" + count: 1 + path: src/pocketmine/item/WrittenBook.php + + - + message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\hell\\\\Nether has an unused parameter \\$options\\.$#" + count: 1 + path: src/pocketmine/level/generator/hell/Nether.php + + - + message: "#^Constructor of class pocketmine\\\\level\\\\generator\\\\normal\\\\Normal has an unused parameter \\$options\\.$#" + count: 1 + path: src/pocketmine/level/generator/normal/Normal.php + + - + message: "#^Used constant pocketmine\\\\RESOURCE_PATH not found\\.$#" + count: 1 + path: src/pocketmine/network/mcpe/protocol/StartGamePacket.php + + - + message: "#^Instantiated class COM not found\\.$#" + count: 2 + path: src/pocketmine/network/upnp/UPnP.php + comment: "only available on Windows" + + - + message: "#^Constructor of class pocketmine\\\\scheduler\\\\TaskScheduler has an unused parameter \\$logger\\.$#" + count: 1 + path: src/pocketmine/scheduler/TaskScheduler.php + + - + message: "#^Variable \\$GLOBALS in isset\\(\\) always exists and is not nullable\\.$#" + path: src/pocketmine/MemoryManager.php + comment: "this isn't defined on threads (thanks pthreads)" + + - + message: "#^Constant pocketmine\\\\COMPOSER_AUTOLOADER_PATH not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\DATA not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\GIT_COMMIT not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\PATH not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\PLUGIN_PATH not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\RESOURCE_PATH not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\START_TIME not found\\.$#" + path: src/pocketmine + + - + message: "#^Constant pocketmine\\\\VERSION not found\\.$#" + path: src/pocketmine + - + message: "#^Used constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#" + path: src/pocketmine/level/format/io/leveldb/LevelDB.php + comment: "explicitly checked" + - + message: "#^Constant LEVELDB_ZLIB_RAW_COMPRESSION not found\\.$#" + path: src/pocketmine/level/format/io/leveldb/LevelDB.php + comment: "explicitly checked" + - + message: "#^Instantiated class LevelDB not found\\.$#" + path: src/pocketmine/level/format/io/leveldb/LevelDB.php + comment: "leveldb extension currently optional" + - + message: "#^Return typehint of method pocketmine\\\\level\\\\format\\\\io\\\\leveldb\\\\LevelDB\\:\\:createDB\\(\\) has invalid type LevelDB\\.$#" + path: src/pocketmine/level/format/io/leveldb/LevelDB.php + - + message: "#^Return typehint of method pocketmine\\\\level\\\\format\\\\io\\\\leveldb\\\\LevelDB\\:\\:getDatabase\\(\\) has invalid type LevelDB\\.$#" + path: src/pocketmine/level/format/io/leveldb/LevelDB.php diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index a18765297..df7d0fb48 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -333,7 +333,7 @@ class CrashDump{ $this->data["general"]["php_os"] = PHP_OS; $this->data["general"]["os"] = Utils::getOS(); $this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]"); - $this->addLine("Git commit: " . GIT_COMMIT); + $this->addLine("Git commit: " . \pocketmine\GIT_COMMIT); $this->addLine("uname -a: " . php_uname("a")); $this->addLine("PHP Version: " . phpversion()); $this->addLine("Zend version: " . zend_version()); diff --git a/src/pocketmine/GlobalConstants.php b/src/pocketmine/GlobalConstants.php new file mode 100644 index 000000000..a9550cc43 --- /dev/null +++ b/src/pocketmine/GlobalConstants.php @@ -0,0 +1,30 @@ + 0){ //If PHP version isn't high enough, anything below might break, so don't bother checking it. return [ - \pocketmine\NAME . " requires PHP >= " . MIN_PHP_VERSION . ", but you have PHP " . PHP_VERSION . "." + "PHP >= " . MIN_PHP_VERSION . " is required, but you have PHP " . PHP_VERSION . "." ]; } $messages = []; if(PHP_INT_SIZE < 8){ - $messages[] = "Running " . \pocketmine\NAME . " with 32-bit systems/PHP is no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system."; + $messages[] = "32-bit systems/PHP are no longer supported. Please upgrade to a 64-bit system, or use a 64-bit PHP binary if this is a 64-bit system."; } if(php_sapi_name() !== "cli"){ - $messages[] = "You must run " . \pocketmine\NAME . " using the CLI."; + $messages[] = "Only PHP CLI is supported."; } $extensions = [ @@ -123,6 +118,29 @@ namespace pocketmine { return $messages; } + function emit_performance_warnings(\Logger $logger){ + if(extension_loaded("xdebug")){ + $logger->warning("Xdebug extension is enabled. This has a major impact on performance."); + } + if(!extension_loaded("pocketmine_chunkutils")){ + $logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance."); + } + if(((int) ini_get('zend.assertions')) !== -1){ + $logger->warning("Debugging assertions are enabled. This may degrade performance. To disable them, set `zend.assertions = -1` in php.ini."); + } + if(\Phar::running(true) === ""){ + $logger->warning("Non-packaged installation detected. This will degrade autoloading speed and make startup times longer."); + } + } + + function set_ini_entries(){ + ini_set("allow_url_fopen", '1'); + ini_set("display_errors", '1'); + ini_set("display_startup_errors", '1'); + ini_set("default_charset", "utf-8"); + ini_set('assert.exception', '1'); + } + function server(){ if(!empty($messages = check_platform_dependencies())){ echo PHP_EOL; @@ -138,6 +156,7 @@ namespace pocketmine { unset($messages); error_reporting(-1); + set_ini_entries(); if(\Phar::running(true) !== ""){ define('pocketmine\PATH', \Phar::running(true) . "/"); @@ -163,20 +182,27 @@ namespace pocketmine { set_error_handler([Utils::class, 'errorExceptionHandler']); - /* - * We now use the Composer autoloader, but this autoloader is still for loading plugins. - */ - $autoloader = new \BaseClassLoader(); - $autoloader->register(false); + $version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER); + define('pocketmine\VERSION', $version->getFullVersion(true)); - set_time_limit(0); //Who set it to 30 seconds?!?! + $gitHash = str_repeat("00", 20); - ini_set("allow_url_fopen", '1'); - ini_set("display_errors", '1'); - ini_set("display_startup_errors", '1'); - ini_set("default_charset", "utf-8"); + if(\Phar::running(true) === ""){ + if(Process::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){ + $gitHash = trim($out); + if(Process::execute("git diff --quiet") === 1 or Utils::execute("git diff --cached --quiet") === 1){ //Locally-modified + $gitHash .= "-dirty"; + } + } + }else{ + $phar = new \Phar(\Phar::running(false)); + $meta = $phar->getMetadata(); + if(isset($meta["git"])){ + $gitHash = $meta["git"]; + } + } - ini_set("memory_limit", '-1'); + define('pocketmine\GIT_COMMIT', $gitHash); define('pocketmine\RESOURCE_PATH', \pocketmine\PATH . 'src' . DIRECTORY_SEPARATOR . 'pocketmine' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR); @@ -189,8 +215,7 @@ namespace pocketmine { mkdir(\pocketmine\DATA, 0777, true); } - define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock'); - define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "a+b")); + define('pocketmine\LOCK_FILE', fopen(\pocketmine\DATA . 'server.lock', "a+b")); if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){ //wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the //other server wrote its PID and released exclusive lock before we get our lock @@ -224,42 +249,7 @@ namespace pocketmine { } unset($tzError); - if(extension_loaded("xdebug")){ - $logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL); - } - if(!extension_loaded("pocketmine_chunkutils")){ - $logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance."); - } - - if(\Phar::running(true) === ""){ - $logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance."); - } - - $version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER); - define('pocketmine\VERSION', $version->getFullVersion(true)); - - $gitHash = str_repeat("00", 20); - - if(\Phar::running(true) === ""){ - if(Process::execute("git rev-parse HEAD", $out) === 0 and $out !== false and strlen($out = trim($out)) === 40){ - $gitHash = trim($out); - if(Process::execute("git diff --quiet") === 1 or Process::execute("git diff --cached --quiet") === 1){ //Locally-modified - $gitHash .= "-dirty"; - } - } - }else{ - $phar = new \Phar(\Phar::running(false)); - $meta = $phar->getMetadata(); - if(isset($meta["git"])){ - $gitHash = $meta["git"]; - } - } - - define('pocketmine\GIT_COMMIT', $gitHash); - - - @define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1); - @ini_set("opcache.mmap_base", bin2hex(random_bytes(8))); //Fix OPCache address errors + emit_performance_warnings($logger); $exitCode = 0; do{ @@ -274,6 +264,13 @@ namespace pocketmine { //TODO: move this to a Server field define('pocketmine\START_TIME', microtime(true)); ThreadManager::init(); + + /* + * We now use the Composer autoloader, but this autoloader is still for loading plugins. + */ + $autoloader = new \BaseClassLoader(); + $autoloader->register(false); + new Server($autoloader, $logger, \pocketmine\DATA, \pocketmine\PLUGIN_PATH); $logger->info("Stopping other threads"); @@ -283,9 +280,7 @@ namespace pocketmine { usleep(10000); //Fixes ServerKiller not being able to start on single-core machines if(ThreadManager::getInstance()->stopAll() > 0){ - if(\pocketmine\DEBUG > 1){ - echo "Some threads could not be stopped, performing a force-kill" . PHP_EOL . PHP_EOL; - } + $logger->debug("Some threads could not be stopped, performing a force-kill"); Process::kill(getmypid()); } }while(false); @@ -298,5 +293,7 @@ namespace pocketmine { exit($exitCode); } - \pocketmine\server(); + if(!defined('pocketmine\_PHPSTAN_ANALYSIS')){ + \pocketmine\server(); + } } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 546f436b6..7342808c7 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1566,12 +1566,6 @@ class Server{ return; } - if(((int) ini_get('zend.assertions')) !== -1){ - $this->logger->warning("Debugging assertions are enabled, this may impact on performance. To disable them, set `zend.assertions = -1` in php.ini."); - } - - ini_set('assert.exception', '1'); - if($this->logger instanceof MainLogger){ $this->logger->setLogDebug(\pocketmine\DEBUG > 1); } diff --git a/src/pocketmine/VersionInfo.php b/src/pocketmine/VersionInfo.php index fd7e7bd86..c8854d58a 100644 --- a/src/pocketmine/VersionInfo.php +++ b/src/pocketmine/VersionInfo.php @@ -21,7 +21,15 @@ namespace pocketmine; +// composer autoload doesn't use require_once and also pthreads can inherit things +// TODO: drop this file and use a final class with constants +if(defined('pocketmine\_VERSION_INFO_INCLUDED')){ + return; +} +const _VERSION_INFO_INCLUDED = true; + + const NAME = "PocketMine-MP"; -const BASE_VERSION = "3.9.7"; +const BASE_VERSION = "3.9.8"; const IS_DEVELOPMENT_BUILD = true; const BUILD_NUMBER = 0; diff --git a/src/pocketmine/command/CommandReader.php b/src/pocketmine/command/CommandReader.php index dfa66ba28..1aaa1721d 100644 --- a/src/pocketmine/command/CommandReader.php +++ b/src/pocketmine/command/CommandReader.php @@ -141,6 +141,7 @@ class CommandReader extends Thread{ case self::TYPE_STREAM: //stream_select doesn't work on piped streams for some reason $r = [self::$stdin]; + $w = $e = null; if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds return true; }elseif($count === false){ //stream error diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php index 8592821af..abce81095 100644 --- a/src/pocketmine/entity/Squid.php +++ b/src/pocketmine/entity/Squid.php @@ -82,7 +82,7 @@ class Squid extends WaterAnimal{ return false; } - if(++$this->switchDirectionTicker === 100 or $this->isCollided){ + if(++$this->switchDirectionTicker === 100){ $this->switchDirectionTicker = 0; if(mt_rand(0, 100) < 50){ $this->swimDirection = null; diff --git a/src/pocketmine/entity/object/Painting.php b/src/pocketmine/entity/object/Painting.php index 70e47307f..f60368519 100644 --- a/src/pocketmine/entity/object/Painting.php +++ b/src/pocketmine/entity/object/Painting.php @@ -90,6 +90,9 @@ class Painting extends Entity{ } public function kill() : void{ + if(!$this->isAlive()){ + return; + } parent::kill(); $drops = true; diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index ba48a129b..32a3629e9 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -378,7 +378,7 @@ class NetworkBinaryStream extends BinaryStream{ } /** - * Writes an EntityUniqueID + * Writes an EntityRuntimeID * * @param int $eid */ diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 89295717e..9af852bba 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -29,6 +29,7 @@ use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\CommandData; use pocketmine\network\mcpe\protocol\types\CommandEnum; use pocketmine\network\mcpe\protocol\types\CommandParameter; +use pocketmine\utils\BinaryDataException; use function count; use function dechex; diff --git a/src/pocketmine/network/mcpe/protocol/DataPacket.php b/src/pocketmine/network/mcpe/protocol/DataPacket.php index 7d074d515..f2e60caa5 100644 --- a/src/pocketmine/network/mcpe/protocol/DataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/DataPacket.php @@ -143,7 +143,7 @@ abstract class DataPacket extends NetworkBinaryStream{ public function __debugInfo(){ $data = []; - foreach($this as $k => $v){ + foreach((array) $this as $k => $v){ if($k === "buffer" and is_string($v)){ $data[$k] = bin2hex($v); }elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){ diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index 9ba73cbb4..606148d1b 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -40,8 +40,8 @@ final class RuntimeBlockMapping{ private static $legacyToRuntimeMap = []; /** @var int[] */ private static $runtimeToLegacyMap = []; - /** @var mixed[] */ - private static $bedrockKnownStates; + /** @var mixed[]|null */ + private static $bedrockKnownStates = null; private function __construct(){ //NOOP @@ -77,6 +77,12 @@ final class RuntimeBlockMapping{ } } + private static function lazyInit() : void{ + if(self::$bedrockKnownStates === null){ + self::init(); + } + } + /** * Randomizes the order of the runtimeID table to prevent plugins relying on them. * Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they @@ -101,6 +107,7 @@ final class RuntimeBlockMapping{ * @return int */ public static function toStaticRuntimeId(int $id, int $meta = 0) : int{ + self::lazyInit(); /* * try id+meta first * if not found, try id+0 (strip meta) @@ -115,6 +122,7 @@ final class RuntimeBlockMapping{ * @return int[] [id, meta] */ public static function fromStaticRuntimeId(int $runtimeId) : array{ + self::lazyInit(); $v = self::$runtimeToLegacyMap[$runtimeId]; return [$v >> 4, $v & 0xf]; } @@ -128,7 +136,7 @@ final class RuntimeBlockMapping{ * @return array */ public static function getBedrockKnownStates() : array{ + self::lazyInit(); return self::$bedrockKnownStates; } } -RuntimeBlockMapping::init(); diff --git a/tests/phpstan/bootstrap.php b/tests/phpstan/bootstrap.php new file mode 100644 index 000000000..10b2da0fc --- /dev/null +++ b/tests/phpstan/bootstrap.php @@ -0,0 +1,24 @@ +