mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-08 04:38:35 +00:00
Fixed dependency handling across plugin loaders (#3971)
This commit is contained in:
parent
ec3986827c
commit
a788954551
42
src/plugin/PluginLoadTriage.php
Normal file
42
src/plugin/PluginLoadTriage.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;
|
||||||
|
|
||||||
|
final class PluginLoadTriage{
|
||||||
|
/**
|
||||||
|
* @var PluginLoadTriageEntry[]
|
||||||
|
* @phpstan-var array<string, PluginLoadTriageEntry>
|
||||||
|
*/
|
||||||
|
public $plugins = [];
|
||||||
|
/**
|
||||||
|
* @var string[][]
|
||||||
|
* @phpstan-var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public $dependencies = [];
|
||||||
|
/**
|
||||||
|
* @var string[][]
|
||||||
|
* @phpstan-var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public $softDependencies = [];
|
||||||
|
}
|
@ -44,7 +44,10 @@ use pocketmine\utils\AssumptionFailedError;
|
|||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use pocketmine\utils\VersionString;
|
use pocketmine\utils\VersionString;
|
||||||
use Webmozart\PathUtil\Path;
|
use Webmozart\PathUtil\Path;
|
||||||
|
use function array_diff_assoc;
|
||||||
use function array_intersect;
|
use function array_intersect;
|
||||||
|
use function array_key_exists;
|
||||||
|
use function array_keys;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
use function class_exists;
|
use function class_exists;
|
||||||
use function count;
|
use function count;
|
||||||
@ -265,18 +268,12 @@ class PluginManager{
|
|||||||
/**
|
/**
|
||||||
* @param string[]|null $newLoaders
|
* @param string[]|null $newLoaders
|
||||||
* @phpstan-param list<class-string<PluginLoader>> $newLoaders
|
* @phpstan-param list<class-string<PluginLoader>> $newLoaders
|
||||||
*
|
|
||||||
* @return Plugin[]
|
|
||||||
*/
|
*/
|
||||||
public function loadPlugins(string $directory, ?array $newLoaders = null) : array{
|
private function triagePlugins(string $directory, PluginLoadTriage $triage, ?array $newLoaders = null) : void{
|
||||||
if(!is_dir($directory)){
|
if(!is_dir($directory)){
|
||||||
return [];
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$plugins = [];
|
|
||||||
$loadedPlugins = [];
|
|
||||||
$dependencies = [];
|
|
||||||
$softDependencies = [];
|
|
||||||
if(is_array($newLoaders)){
|
if(is_array($newLoaders)){
|
||||||
$loaders = [];
|
$loaders = [];
|
||||||
foreach($newLoaders as $key){
|
foreach($newLoaders as $key){
|
||||||
@ -320,7 +317,7 @@ class PluginManager{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($plugins[$name]) or $this->getPlugin($name) instanceof Plugin){
|
if(isset($triage->plugins[$name]) or $this->getPlugin($name) instanceof Plugin){
|
||||||
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_duplicateError($name)));
|
$this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_duplicateError($name)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -336,77 +333,129 @@ class PluginManager{
|
|||||||
)));
|
)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$plugins[$name] = new PluginLoadTriageEntry($file, $loader, $description);
|
|
||||||
|
|
||||||
$softDependencies[$name] = array_merge($softDependencies[$name] ?? [], $description->getSoftDepend());
|
$triage->plugins[$name] = new PluginLoadTriageEntry($file, $loader, $description);
|
||||||
$dependencies[$name] = $description->getDepend();
|
|
||||||
|
$triage->softDependencies[$name] = array_merge($triage->softDependencies[$name] ?? [], $description->getSoftDepend());
|
||||||
|
$triage->dependencies[$name] = $description->getDepend();
|
||||||
|
|
||||||
foreach($description->getLoadBefore() as $before){
|
foreach($description->getLoadBefore() as $before){
|
||||||
if(isset($softDependencies[$before])){
|
if(isset($triage->softDependencies[$before])){
|
||||||
$softDependencies[$before][] = $name;
|
$triage->softDependencies[$before][] = $name;
|
||||||
}else{
|
}else{
|
||||||
$softDependencies[$before] = [$name];
|
$triage->softDependencies[$before] = [$name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while(count($plugins) > 0){
|
/**
|
||||||
|
* @param string[][] $dependencyLists
|
||||||
|
* @param Plugin[] $loadedPlugins
|
||||||
|
*/
|
||||||
|
private function checkDepsForTriage(string $pluginName, string $dependencyType, array &$dependencyLists, array $loadedPlugins, PluginLoadTriage $triage) : void{
|
||||||
|
if(isset($dependencyLists[$pluginName])){
|
||||||
|
foreach($dependencyLists[$pluginName] as $key => $dependency){
|
||||||
|
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
||||||
|
$this->server->getLogger()->debug("Successfully resolved $dependencyType dependency \"$dependency\" for plugin \"$pluginName\"");
|
||||||
|
unset($dependencyLists[$pluginName][$key]);
|
||||||
|
}elseif(array_key_exists($dependency, $triage->plugins)){
|
||||||
|
$this->server->getLogger()->debug("Deferring resolution of $dependencyType dependency \"$dependency\" for plugin \"$pluginName\" (found but not loaded yet)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($dependencyLists[$pluginName]) === 0){
|
||||||
|
unset($dependencyLists[$pluginName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Plugin[]
|
||||||
|
*/
|
||||||
|
public function loadPlugins(string $directory) : array{
|
||||||
|
$triage = new PluginLoadTriage();
|
||||||
|
$this->triagePlugins($directory, $triage);
|
||||||
|
|
||||||
|
$loadedPlugins = [];
|
||||||
|
|
||||||
|
while(count($triage->plugins) > 0){
|
||||||
$loadedThisLoop = 0;
|
$loadedThisLoop = 0;
|
||||||
foreach($plugins as $name => $entry){
|
foreach($triage->plugins as $name => $entry){
|
||||||
if(isset($dependencies[$name])){
|
$this->checkDepsForTriage($name, "hard", $triage->dependencies, $loadedPlugins, $triage);
|
||||||
foreach($dependencies[$name] as $key => $dependency){
|
$this->checkDepsForTriage($name, "soft", $triage->softDependencies, $loadedPlugins, $triage);
|
||||||
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
|
||||||
unset($dependencies[$name][$key]);
|
|
||||||
}elseif(!isset($plugins[$dependency])){
|
|
||||||
$this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
|
||||||
$name,
|
|
||||||
KnownTranslationFactory::pocketmine_plugin_unknownDependency($dependency)
|
|
||||||
)));
|
|
||||||
unset($plugins[$name]);
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($dependencies[$name]) === 0){
|
if(!isset($triage->dependencies[$name]) and !isset($triage->softDependencies[$name])){
|
||||||
unset($dependencies[$name]);
|
unset($triage->plugins[$name]);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($softDependencies[$name])){
|
|
||||||
foreach($softDependencies[$name] as $key => $dependency){
|
|
||||||
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
|
||||||
$this->server->getLogger()->debug("Successfully resolved soft dependency \"$dependency\" for plugin \"$name\"");
|
|
||||||
unset($softDependencies[$name][$key]);
|
|
||||||
}elseif(!isset($plugins[$dependency])){
|
|
||||||
//this dependency is never going to be resolved, so don't bother trying
|
|
||||||
$this->server->getLogger()->debug("Skipping resolution of missing soft dependency \"$dependency\" for plugin \"$name\"");
|
|
||||||
unset($softDependencies[$name][$key]);
|
|
||||||
}else{
|
|
||||||
$this->server->getLogger()->debug("Deferring resolution of soft dependency \"$dependency\" for plugin \"$name\" (found but not loaded yet)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($softDependencies[$name]) === 0){
|
|
||||||
unset($softDependencies[$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){
|
|
||||||
unset($plugins[$name]);
|
|
||||||
$loadedThisLoop++;
|
$loadedThisLoop++;
|
||||||
|
|
||||||
|
$oldRegisteredLoaders = $this->fileAssociations;
|
||||||
if(($plugin = $this->internalLoadPlugin($entry->getFile(), $entry->getLoader(), $entry->getDescription())) instanceof Plugin){
|
if(($plugin = $this->internalLoadPlugin($entry->getFile(), $entry->getLoader(), $entry->getDescription())) instanceof Plugin){
|
||||||
$loadedPlugins[$name] = $plugin;
|
$loadedPlugins[$name] = $plugin;
|
||||||
|
$diffLoaders = [];
|
||||||
|
foreach($this->fileAssociations as $k => $loader){
|
||||||
|
if(!array_key_exists($k, $oldRegisteredLoaders)){
|
||||||
|
$diffLoaders[] = $k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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($directory, $triage, $diffLoaders);
|
||||||
|
$diffPlugins = array_diff_assoc($triage->plugins, $plugins);
|
||||||
|
$this->server->getLogger()->debug("Re-triage found plugins: " . implode(", ", array_keys($diffPlugins)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($loadedThisLoop === 0){
|
if($loadedThisLoop === 0){
|
||||||
//No plugins loaded :(
|
//No plugins loaded :(
|
||||||
foreach($plugins as $name => $file){
|
|
||||||
|
//check for skippable soft dependencies first, in case the dependents could resolve hard dependencies
|
||||||
|
foreach($triage->plugins as $name => $file){
|
||||||
|
if(isset($triage->softDependencies[$name]) && !isset($triage->dependencies[$name])){
|
||||||
|
foreach($triage->softDependencies[$name] as $k => $dependency){
|
||||||
|
if($this->getPlugin($dependency) === null && !array_key_exists($dependency, $triage->plugins)){
|
||||||
|
$this->server->getLogger()->debug("Skipping resolution of missing soft dependency \"$dependency\" for plugin \"$name\"");
|
||||||
|
unset($triage->softDependencies[$name][$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(count($triage->softDependencies[$name]) === 0){
|
||||||
|
unset($triage->softDependencies[$name]);
|
||||||
|
continue 2; //go back to the top and try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($triage->plugins as $name => $file){
|
||||||
|
if(isset($triage->dependencies[$name])){
|
||||||
|
$unknownDependencies = [];
|
||||||
|
|
||||||
|
foreach($triage->dependencies[$name] as $k => $dependency){
|
||||||
|
if($this->getPlugin($dependency) === null && !array_key_exists($dependency, $triage->plugins)){
|
||||||
|
//assume that the plugin is never going to be loaded
|
||||||
|
//by this point all soft dependencies have been ignored if they were able to be, so
|
||||||
|
//there's no chance of this dependency ever being resolved
|
||||||
|
$unknownDependencies[$dependency] = $dependency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($unknownDependencies) > 0){
|
||||||
|
$this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError(
|
||||||
|
$name,
|
||||||
|
KnownTranslationFactory::pocketmine_plugin_unknownDependency(implode(", ", $unknownDependencies))
|
||||||
|
)));
|
||||||
|
unset($triage->plugins[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($triage->plugins as $name => $file){
|
||||||
$this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, KnownTranslationFactory::pocketmine_plugin_circularDependency())));
|
$this->server->getLogger()->critical($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_loadError($name, KnownTranslationFactory::pocketmine_plugin_circularDependency())));
|
||||||
}
|
}
|
||||||
$plugins = [];
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit dfbea943e1c64094358acac56013b89065884657
|
Subproject commit db184c25636b1f984c6539fd4b1729b0b64b35d6
|
Loading…
x
Reference in New Issue
Block a user