diff --git a/.github/readme/pocketmine-dark.png b/.github/readme/pocketmine-dark.png new file mode 100644 index 0000000000..a82b96c120 Binary files /dev/null and b/.github/readme/pocketmine-dark.png differ diff --git a/.github/readme/pocketmine.png b/.github/readme/pocketmine.png new file mode 100644 index 0000000000..9a340203a3 Binary files /dev/null and b/.github/readme/pocketmine.png differ diff --git a/README.md b/README.md index 0f91b11423..a1075f1aa7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@

-
+ + + + + + +
A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP

diff --git a/composer.json b/composer.json index 6d98d28762..001acdafdc 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "pocketmine/classloader": "^0.2.0", "pocketmine/color": "^0.2.0", "pocketmine/errorhandler": "^0.6.0", - "pocketmine/locale-data": "~2.7.0", + "pocketmine/locale-data": "~2.8.0", "pocketmine/log": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0", "pocketmine/math": "^0.4.0", diff --git a/composer.lock b/composer.lock index 9efe7674eb..95591e8e47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f3349dee8c1925a1890757897b25a2dc", + "content-hash": "0d40189fe566643df70bf3eee5a9b126", "packages": [ { "name": "adhocore/json-comment", @@ -563,16 +563,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.7.0", + "version": "2.8.3", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "f00216c4709d2c5a2af478498315206b336b8e2e" + "reference": "113c115a3b8976917eb22b74dccab464831b6483" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/f00216c4709d2c5a2af478498315206b336b8e2e", - "reference": "f00216c4709d2c5a2af478498315206b336b8e2e", + "url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483", + "reference": "113c115a3b8976917eb22b74dccab464831b6483", "shasum": "" }, "type": "library", @@ -580,9 +580,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.7.0" + "source": "https://github.com/pmmp/Language/tree/2.8.3" }, - "time": "2022-05-10T13:29:27+00:00" + "time": "2022-05-11T13:51:37+00:00" }, { "name": "pocketmine/log", diff --git a/src/Server.php b/src/Server.php index 7c4202f9ee..a2e399f4e6 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1003,14 +1003,29 @@ class Server{ register_shutdown_function([$this, "crashDump"]); - $this->pluginManager->loadPlugins($this->pluginPath); - $this->enablePlugins(PluginEnableOrder::STARTUP()); + $loadErrorCount = 0; + $this->pluginManager->loadPlugins($this->pluginPath, $loadErrorCount); + if($loadErrorCount > 0){ + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someLoadErrors())); + $this->forceShutdown(); + return; + } + if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){ + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors())); + $this->forceShutdown(); + return; + } if(!$this->startupPrepareWorlds()){ $this->forceShutdown(); return; } - $this->enablePlugins(PluginEnableOrder::POSTWORLD()); + + if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){ + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors())); + $this->forceShutdown(); + return; + } if(!$this->startupPrepareNetworkInterfaces()){ $this->forceShutdown(); @@ -1097,6 +1112,7 @@ class Server{ $generatorClass = $getGenerator($generatorName, $generatorOptions, $name); if($generatorClass === null){ + $anyWorldFailedToLoad = true; continue; } $creationOptions->setGeneratorClass($generatorClass); @@ -1133,16 +1149,19 @@ class Server{ $generatorName = $this->configGroup->getConfigString("level-type"); $generatorOptions = $this->configGroup->getConfigString("generator-settings"); $generatorClass = $getGenerator($generatorName, $generatorOptions, $default); - if($generatorClass !== null){ - $creationOptions = WorldCreationOptions::create() - ->setGeneratorClass($generatorClass) - ->setGeneratorOptions($generatorOptions); - $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed")); - if($convertedSeed !== null){ - $creationOptions->setSeed($convertedSeed); - } - $this->worldManager->generateWorld($default, $creationOptions); + + if($generatorClass === null){ + $this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError())); + return false; } + $creationOptions = WorldCreationOptions::create() + ->setGeneratorClass($generatorClass) + ->setGeneratorOptions($generatorOptions); + $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed")); + if($convertedSeed !== null){ + $creationOptions->setSeed($convertedSeed); + } + $this->worldManager->generateWorld($default, $creationOptions); } $world = $this->worldManager->getWorldByName($default); @@ -1391,16 +1410,21 @@ class Server{ } } - public function enablePlugins(PluginEnableOrder $type) : void{ + public function enablePlugins(PluginEnableOrder $type) : bool{ + $allSuccess = true; foreach($this->pluginManager->getPlugins() as $plugin){ if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){ - $this->pluginManager->enablePlugin($plugin); + if(!$this->pluginManager->enablePlugin($plugin)){ + $allSuccess = false; + } } } if($type->equals(PluginEnableOrder::POSTWORLD())){ $this->commandMap->registerServerAliases(); } + + return $allSuccess; } /** @@ -1440,7 +1464,7 @@ class Server{ } if($this->isRunning){ - $this->logger->emergency("Forcing server shutdown"); + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_forcingShutdown())); } try{ if(!$this->isRunning()){ diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 6458215ba3..c780371efb 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -1860,6 +1860,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_RESTRICTEDNAME, []); } + public static function pocketmine_plugin_someEnableErrors() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_SOMEENABLEERRORS, []); + } + + public static function pocketmine_plugin_someLoadErrors() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_SOMELOADERRORS, []); + } + public static function pocketmine_plugin_spacesDiscouraged(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_SPACESDISCOURAGED, [ 0 => $param0, @@ -1958,6 +1966,10 @@ final class KnownTranslationFactory{ ]); } + public static function pocketmine_server_forcingShutdown() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_FORCINGSHUTDOWN, []); + } + public static function pocketmine_server_info(Translatable|string $param0, Translatable|string $param1) : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_SERVER_INFO, [ 0 => $param0, diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 9201f4b6f8..daa20a17f8 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -385,6 +385,8 @@ final class KnownTranslationKeys{ public const POCKETMINE_PLUGIN_MAINCLASSNOTFOUND = "pocketmine.plugin.mainClassNotFound"; public const POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE = "pocketmine.plugin.mainClassWrongType"; public const POCKETMINE_PLUGIN_RESTRICTEDNAME = "pocketmine.plugin.restrictedName"; + public const POCKETMINE_PLUGIN_SOMEENABLEERRORS = "pocketmine.plugin.someEnableErrors"; + public const POCKETMINE_PLUGIN_SOMELOADERRORS = "pocketmine.plugin.someLoadErrors"; public const POCKETMINE_PLUGIN_SPACESDISCOURAGED = "pocketmine.plugin.spacesDiscouraged"; public const POCKETMINE_PLUGIN_SUICIDE = "pocketmine.plugin.suicide"; public const POCKETMINE_PLUGIN_UNKNOWNDEPENDENCY = "pocketmine.plugin.unknownDependency"; @@ -405,6 +407,7 @@ final class KnownTranslationKeys{ public const POCKETMINE_SERVER_DEVBUILD_WARNING2 = "pocketmine.server.devBuild.warning2"; public const POCKETMINE_SERVER_DEVBUILD_WARNING3 = "pocketmine.server.devBuild.warning3"; public const POCKETMINE_SERVER_DONATE = "pocketmine.server.donate"; + public const POCKETMINE_SERVER_FORCINGSHUTDOWN = "pocketmine.server.forcingShutdown"; public const POCKETMINE_SERVER_INFO = "pocketmine.server.info"; public const POCKETMINE_SERVER_INFO_EXTENDED = "pocketmine.server.info.extended"; public const POCKETMINE_SERVER_LICENSE = "pocketmine.server.license"; diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 47f3a257bb..2f3e0d1ea7 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -138,7 +138,7 @@ class PluginManager{ $dataFolder = $this->getDataDirectory($path, $description->getName()); if(file_exists($dataFolder) && !is_dir($dataFolder)){ - $this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $description->getName(), KnownTranslationFactory::pocketmine_plugin_badDataFolder($dataFolder) ))); @@ -153,14 +153,14 @@ class PluginManager{ $mainClass = $description->getMain(); if(!class_exists($mainClass, true)){ - $this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $description->getName(), KnownTranslationFactory::pocketmine_plugin_mainClassNotFound() ))); return null; } if(!is_a($mainClass, Plugin::class, true)){ - $this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $description->getName(), KnownTranslationFactory::pocketmine_plugin_mainClassWrongType(Plugin::class) ))); @@ -168,7 +168,7 @@ class PluginManager{ } $reflect = new \ReflectionClass($mainClass); //this shouldn't throw; we already checked that it exists if(!$reflect->isInstantiable()){ - $this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $description->getName(), KnownTranslationFactory::pocketmine_plugin_mainClassAbstract() ))); @@ -179,7 +179,7 @@ class PluginManager{ foreach($description->getPermissions() as $permsGroup){ foreach($permsGroup as $perm){ if($permManager->getPermission($perm->getName()) !== null){ - $this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $description->getName(), KnownTranslationFactory::pocketmine_plugin_duplicatePermissionError($perm->getName()) ))); @@ -229,7 +229,7 @@ class PluginManager{ * @param string[]|null $newLoaders * @phpstan-param list> $newLoaders */ - private function triagePlugins(string $path, PluginLoadTriage $triage, ?array $newLoaders = null) : void{ + private function triagePlugins(string $path, PluginLoadTriage $triage, int &$loadErrorCount, ?array $newLoaders = null) : void{ if(is_array($newLoaders)){ $loaders = []; foreach($newLoaders as $key){ @@ -261,14 +261,16 @@ class PluginManager{ try{ $description = $loader->getPluginDescription($file); }catch(PluginDescriptionParseException $e){ - $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError( + $this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError( $file, KnownTranslationFactory::pocketmine_plugin_invalidManifest($e->getMessage()) ))); + $loadErrorCount++; continue; }catch(\RuntimeException $e){ //TODO: more specific exception handling - $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($file, $e->getMessage()))); + $this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($file, $e->getMessage()))); $this->server->getLogger()->logException($e); + $loadErrorCount++; continue; } if($description === null){ @@ -278,12 +280,14 @@ class PluginManager{ $name = $description->getName(); if(($loadabilityError = $loadabilityChecker->check($description)) !== null){ - $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, $loadabilityError))); + $this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, $loadabilityError))); + $loadErrorCount++; continue; } if(isset($triage->plugins[$name]) || $this->getPlugin($name) instanceof Plugin){ - $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_duplicateError($name))); + $this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_duplicateError($name))); + $loadErrorCount++; continue; } @@ -296,6 +300,9 @@ class PluginManager{ $name, $this->graylist->isWhitelist() ? KnownTranslationFactory::pocketmine_plugin_disallowedByWhitelist() : KnownTranslationFactory::pocketmine_plugin_disallowedByBlacklist() ))); + //this does NOT increment loadErrorCount, because using the graylist to prevent a plugin from + //loading is not considered accidental; this is the same as if the plugin were manually removed + //this means that the server will continue to boot even if some plugins were blocked by graylist continue; } @@ -339,14 +346,14 @@ class PluginManager{ /** * @return Plugin[] */ - public function loadPlugins(string $path) : array{ + public function loadPlugins(string $path, int &$loadErrorCount = 0) : array{ if($this->loadPluginsGuard){ throw new \LogicException(__METHOD__ . "() cannot be called from within itself"); } $this->loadPluginsGuard = true; $triage = new PluginLoadTriage(); - $this->triagePlugins($path, $triage); + $this->triagePlugins($path, $triage, $loadErrorCount); $loadedPlugins = []; @@ -372,10 +379,12 @@ class PluginManager{ if(count($diffLoaders) !== 0){ $this->server->getLogger()->debug("Plugin $name registered a new plugin loader during load, scanning for new plugins"); $plugins = $triage->plugins; - $this->triagePlugins($path, $triage, $diffLoaders); + $this->triagePlugins($path, $triage, $loadErrorCount, $diffLoaders); $diffPlugins = array_diff_key($triage->plugins, $plugins); $this->server->getLogger()->debug("Re-triage found plugins: " . implode(", ", array_keys($diffPlugins))); } + }else{ + $loadErrorCount++; } } } @@ -418,12 +427,14 @@ class PluginManager{ KnownTranslationFactory::pocketmine_plugin_unknownDependency(implode(", ", $unknownDependencies)) ))); unset($triage->plugins[$name]); + $loadErrorCount++; } } } foreach(Utils::stringifyKeys($triage->plugins) as $name => $file){ $this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, KnownTranslationFactory::pocketmine_plugin_circularDependency()))); + $loadErrorCount++; } break; } @@ -437,7 +448,7 @@ class PluginManager{ return isset($this->plugins[$plugin->getDescription()->getName()]) && $plugin->isEnabled(); } - public function enablePlugin(Plugin $plugin) : void{ + public function enablePlugin(Plugin $plugin) : bool{ if(!$plugin->isEnabled()){ $this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_enable($plugin->getDescription()->getFullName()))); @@ -447,6 +458,8 @@ class PluginManager{ $this->enabledPlugins[$plugin->getDescription()->getName()] = $plugin; (new PluginEnableEvent($plugin))->call(); + + return true; }else{ $this->server->getLogger()->critical($this->server->getLanguage()->translate( KnownTranslationFactory::pocketmine_plugin_enableError( @@ -454,8 +467,12 @@ class PluginManager{ KnownTranslationFactory::pocketmine_plugin_suicide() ) )); + + return false; } } + + return true; //TODO: maybe this should be an error? } public function disablePlugins() : void{