mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
PluginManager: Improved startup performance when loading many plugins
for some reason we were reading and parsing the plugin.yml at least twice for every plugin loaded. We were repeating work already done by the initial loadPlugins() triage (discovering correct loader, loading plugin.yml from disk, parsing plugin.yml, validating plugin.yml) every time loadPlugin() was called with that plugin.
This commit is contained in:
parent
e1ee320c8d
commit
6d728e8d98
42
src/plugin/PluginLoadTriageEntry.php
Normal file
42
src/plugin/PluginLoadTriageEntry.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class PluginLoadTriageEntry{
|
||||
|
||||
public function __construct(
|
||||
private string $file,
|
||||
private PluginLoader $loader,
|
||||
private PluginDescription $description
|
||||
){}
|
||||
|
||||
public function getFile() : string{ return $this->file; }
|
||||
|
||||
public function getLoader() : PluginLoader{ return $this->loader; }
|
||||
|
||||
public function getDescription() : PluginDescription{ return $this->description; }
|
||||
}
|
@ -178,77 +178,7 @@ class PluginManager{
|
||||
if($loader->canLoadPlugin($path)){
|
||||
$description = $loader->getPluginDescription($path);
|
||||
if($description instanceof PluginDescription){
|
||||
$language = $this->server->getLanguage();
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_load($description->getFullName())));
|
||||
|
||||
$dataFolder = $this->getDataDirectory($path, $description->getName());
|
||||
if(file_exists($dataFolder) and !is_dir($dataFolder)){
|
||||
$this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_badDataFolder($dataFolder)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
if(!file_exists($dataFolder)){
|
||||
mkdir($dataFolder, 0777, true);
|
||||
}
|
||||
|
||||
$prefixed = $loader->getAccessProtocol() . $path;
|
||||
$loader->loadPlugin($prefixed);
|
||||
|
||||
$mainClass = $description->getMain();
|
||||
if(!class_exists($mainClass, true)){
|
||||
$this->server->getLogger()->error($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(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_mainClassWrongType(Plugin::class)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
|
||||
$permManager = PermissionManager::getInstance();
|
||||
$opRoot = $permManager->getPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$everyoneRoot = $permManager->getPermission(DefaultPermissions::ROOT_USER);
|
||||
foreach($description->getPermissions() as $default => $perms){
|
||||
foreach($perms as $perm){
|
||||
$permManager->addPermission($perm);
|
||||
switch($default){
|
||||
case PermissionParser::DEFAULT_TRUE:
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_OP:
|
||||
$opRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_NOT_OP:
|
||||
//TODO: I don't think anyone uses this, and it currently relies on some magic inside PermissibleBase
|
||||
//to ensure that the operator override actually applies.
|
||||
//Explore getting rid of this.
|
||||
//The following grants this permission to anyone who has the "everyone" root permission.
|
||||
//However, if the operator root node (which has higher priority) is present, the
|
||||
//permission will be denied instead.
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
$opRoot->addChild($perm->getName(), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Plugin $plugin
|
||||
* @see Plugin::__construct()
|
||||
*/
|
||||
$plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, new DiskResourceProvider($prefixed . "/resources/"));
|
||||
$this->plugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
return $plugin;
|
||||
$this->internalLoadPlugin($path, $loader, $description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,6 +186,80 @@ class PluginManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
private function internalLoadPlugin(string $path, PluginLoader $loader, PluginDescription $description) : ?Plugin{
|
||||
$language = $this->server->getLanguage();
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_load($description->getFullName())));
|
||||
|
||||
$dataFolder = $this->getDataDirectory($path, $description->getName());
|
||||
if(file_exists($dataFolder) and !is_dir($dataFolder)){
|
||||
$this->server->getLogger()->error($language->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_badDataFolder($dataFolder)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
if(!file_exists($dataFolder)){
|
||||
mkdir($dataFolder, 0777, true);
|
||||
}
|
||||
|
||||
$prefixed = $loader->getAccessProtocol() . $path;
|
||||
$loader->loadPlugin($prefixed);
|
||||
|
||||
$mainClass = $description->getMain();
|
||||
if(!class_exists($mainClass, true)){
|
||||
$this->server->getLogger()->error($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(
|
||||
$description->getName(),
|
||||
KnownTranslationFactory::pocketmine_plugin_mainClassWrongType(Plugin::class)
|
||||
)));
|
||||
return null;
|
||||
}
|
||||
|
||||
$permManager = PermissionManager::getInstance();
|
||||
$opRoot = $permManager->getPermission(DefaultPermissions::ROOT_OPERATOR);
|
||||
$everyoneRoot = $permManager->getPermission(DefaultPermissions::ROOT_USER);
|
||||
foreach($description->getPermissions() as $default => $perms){
|
||||
foreach($perms as $perm){
|
||||
$permManager->addPermission($perm);
|
||||
switch($default){
|
||||
case PermissionParser::DEFAULT_TRUE:
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_OP:
|
||||
$opRoot->addChild($perm->getName(), true);
|
||||
break;
|
||||
case PermissionParser::DEFAULT_NOT_OP:
|
||||
//TODO: I don't think anyone uses this, and it currently relies on some magic inside PermissibleBase
|
||||
//to ensure that the operator override actually applies.
|
||||
//Explore getting rid of this.
|
||||
//The following grants this permission to anyone who has the "everyone" root permission.
|
||||
//However, if the operator root node (which has higher priority) is present, the
|
||||
//permission will be denied instead.
|
||||
$everyoneRoot->addChild($perm->getName(), true);
|
||||
$opRoot->addChild($perm->getName(), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Plugin $plugin
|
||||
* @see Plugin::__construct()
|
||||
*/
|
||||
$plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, new DiskResourceProvider($prefixed . "/resources/"));
|
||||
$this->plugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]|null $newLoaders
|
||||
* @phpstan-param list<class-string<PluginLoader>> $newLoaders
|
||||
@ -330,7 +334,7 @@ class PluginManager{
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
$plugins[$name] = $file;
|
||||
$plugins[$name] = new PluginLoadTriageEntry($file, $loader, $description);
|
||||
|
||||
$softDependencies[$name] = array_merge($softDependencies[$name] ?? [], $description->getSoftDepend());
|
||||
$dependencies[$name] = $description->getDepend();
|
||||
@ -347,7 +351,7 @@ class PluginManager{
|
||||
|
||||
while(count($plugins) > 0){
|
||||
$loadedThisLoop = 0;
|
||||
foreach($plugins as $name => $file){
|
||||
foreach($plugins as $name => $entry){
|
||||
if(isset($dependencies[$name])){
|
||||
foreach($dependencies[$name] as $key => $dependency){
|
||||
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
||||
@ -389,7 +393,7 @@ class PluginManager{
|
||||
if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){
|
||||
unset($plugins[$name]);
|
||||
$loadedThisLoop++;
|
||||
if(($plugin = $this->loadPlugin($file, $loaders)) instanceof Plugin){
|
||||
if(($plugin = $this->internalLoadPlugin($entry->getFile(), $entry->getLoader(), $entry->getDescription())) instanceof Plugin){
|
||||
$loadedPlugins[$name] = $plugin;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user