mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-10-19 04:15:04 +00:00
Removed pocketmine subdirectory, map PSR-4 style
This commit is contained in:
103
src/plugin/ApiVersion.php
Normal file
103
src/plugin/ApiVersion.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\utils\VersionString;
|
||||
use function array_map;
|
||||
use function array_push;
|
||||
use function count;
|
||||
use function usort;
|
||||
|
||||
final class ApiVersion{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $myVersionStr
|
||||
* @param string[] $wantVersionsStr
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isCompatible(string $myVersionStr, array $wantVersionsStr) : bool{
|
||||
$myVersion = new VersionString($myVersionStr);
|
||||
foreach($wantVersionsStr as $versionStr){
|
||||
$version = new VersionString($versionStr);
|
||||
//Format: majorVersion.minorVersion.patch (3.0.0)
|
||||
// or: majorVersion.minorVersion.patch-devBuild (3.0.0-alpha1)
|
||||
if($version->getBaseVersion() !== $myVersion->getBaseVersion()){
|
||||
if($version->getMajor() !== $myVersion->getMajor() or $version->getSuffix() !== $myVersion->getSuffix()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($version->getMinor() > $myVersion->getMinor()){ //If the plugin requires new API features, being backwards compatible
|
||||
continue;
|
||||
}
|
||||
|
||||
if($version->getMinor() === $myVersion->getMinor() and $version->getPatch() > $myVersion->getPatch()){ //If the plugin requires bug fixes in patches, being backwards compatible
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $versions
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function checkAmbiguousVersions(array $versions) : array{
|
||||
/** @var VersionString[][] $indexedVersions */
|
||||
$indexedVersions = [];
|
||||
|
||||
foreach($versions as $str){
|
||||
$v = new VersionString($str);
|
||||
if($v->getSuffix() !== ""){ //suffix is always unambiguous
|
||||
continue;
|
||||
}
|
||||
if(!isset($indexedVersions[$v->getMajor()])){
|
||||
$indexedVersions[$v->getMajor()] = [$v];
|
||||
}else{
|
||||
$indexedVersions[$v->getMajor()][] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var VersionString[] $result */
|
||||
$result = [];
|
||||
foreach($indexedVersions as $major => $list){
|
||||
if(count($list) > 1){
|
||||
array_push($result, ...$list);
|
||||
}
|
||||
}
|
||||
|
||||
usort($result, static function(VersionString $string1, VersionString $string2){ return $string1->compare($string2); });
|
||||
|
||||
return array_map(static function(VersionString $string){ return $string->getBaseVersion(); }, $result);
|
||||
}
|
||||
}
|
83
src/plugin/DiskResourceProvider.php
Normal file
83
src/plugin/DiskResourceProvider.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?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;
|
||||
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function is_dir;
|
||||
use function rtrim;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Provides resources from the given plugin directory on disk. The path may be prefixed with a specific access protocol
|
||||
* to enable special types of access.
|
||||
*/
|
||||
class DiskResourceProvider implements ResourceProvider{
|
||||
|
||||
/** @var string */
|
||||
private $file;
|
||||
|
||||
public function __construct(string $path){
|
||||
$this->file = rtrim($path, "/\\") . "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an embedded resource on the plugin file.
|
||||
* WARNING: You must close the resource given using fclose()
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return null|resource Resource data, or null
|
||||
*/
|
||||
public function getResource(string $filename){
|
||||
$filename = rtrim(str_replace("\\", "/", $filename), "/");
|
||||
if(file_exists($this->file . "/resources/" . $filename)){
|
||||
return fopen($this->file . "/resources/" . $filename, "rb");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the resources packaged with the plugin in the form ["path/in/resources" => SplFileInfo]
|
||||
*
|
||||
* @return \SplFileInfo[]
|
||||
*/
|
||||
public function getResources() : array{
|
||||
$resources = [];
|
||||
if(is_dir($this->file . "resources/")){
|
||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->file . "resources/")) as $resource){
|
||||
if($resource->isFile()){
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, "/", substr((string) $resource, strlen($this->file . "resources/")));
|
||||
$resources[$path] = $resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $resources;
|
||||
}
|
||||
}
|
78
src/plugin/PharPluginLoader.php
Normal file
78
src/plugin/PharPluginLoader.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?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;
|
||||
|
||||
use function is_file;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Handles different types of plugins
|
||||
*/
|
||||
class PharPluginLoader implements PluginLoader{
|
||||
|
||||
/** @var \ClassLoader */
|
||||
private $loader;
|
||||
|
||||
public function __construct(\ClassLoader $loader){
|
||||
$this->loader = $loader;
|
||||
}
|
||||
|
||||
public function canLoadPlugin(string $path) : bool{
|
||||
$ext = ".phar";
|
||||
return is_file($path) and substr($path, -strlen($ext)) === $ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the plugin contained in $file
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public function loadPlugin(string $file) : void{
|
||||
$this->loader->addPath("$file/src");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PluginDescription from the file
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return null|PluginDescription
|
||||
*/
|
||||
public function getPluginDescription(string $file) : ?PluginDescription{
|
||||
$phar = new \Phar($file);
|
||||
if(isset($phar["plugin.yml"])){
|
||||
$pluginYml = $phar["plugin.yml"];
|
||||
if($pluginYml instanceof \PharFileInfo){
|
||||
return new PluginDescription($pluginYml->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAccessProtocol() : string{
|
||||
return "phar://";
|
||||
}
|
||||
}
|
98
src/plugin/Plugin.php
Normal file
98
src/plugin/Plugin.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?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);
|
||||
|
||||
/**
|
||||
* Plugin related classes
|
||||
*/
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\scheduler\TaskScheduler;
|
||||
use pocketmine\Server;
|
||||
|
||||
/**
|
||||
* It is recommended to use PluginBase for the actual plugin
|
||||
*/
|
||||
interface Plugin{
|
||||
|
||||
public function __construct(PluginLoader $loader, Server $server, PluginDescription $description, string $dataFolder, string $file, ResourceProvider $resourceProvider);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled() : bool;
|
||||
|
||||
/**
|
||||
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
|
||||
*
|
||||
* @internal This is intended for core use only and should not be used by plugins
|
||||
* @see PluginManager::enablePlugin()
|
||||
* @see PluginManager::disablePlugin()
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function onEnableStateChange(bool $enabled) : void;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled() : bool;
|
||||
|
||||
/**
|
||||
* Gets the plugin's data folder to save files and configuration.
|
||||
* This directory name has a trailing slash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDataFolder() : string;
|
||||
|
||||
/**
|
||||
* @return PluginDescription
|
||||
*/
|
||||
public function getDescription() : PluginDescription;
|
||||
|
||||
/**
|
||||
* @return Server
|
||||
*/
|
||||
public function getServer() : Server;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName() : string;
|
||||
|
||||
/**
|
||||
* @return PluginLogger
|
||||
*/
|
||||
public function getLogger() : PluginLogger;
|
||||
|
||||
/**
|
||||
* @return PluginLoader
|
||||
*/
|
||||
public function getPluginLoader() : PluginLoader;
|
||||
|
||||
/**
|
||||
* @return TaskScheduler
|
||||
*/
|
||||
public function getScheduler() : TaskScheduler;
|
||||
|
||||
}
|
380
src/plugin/PluginBase.php
Normal file
380
src/plugin/PluginBase.php
Normal file
@@ -0,0 +1,380 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandExecutor;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\PluginCommand;
|
||||
use pocketmine\command\PluginIdentifiableCommand;
|
||||
use pocketmine\scheduler\TaskScheduler;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Config;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fclose;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_string;
|
||||
use function mkdir;
|
||||
use function rtrim;
|
||||
use function stream_copy_to_stream;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
|
||||
/** @var PluginLoader */
|
||||
private $loader;
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
|
||||
/** @var bool */
|
||||
private $isEnabled = false;
|
||||
|
||||
/** @var PluginDescription */
|
||||
private $description;
|
||||
|
||||
/** @var string */
|
||||
private $dataFolder;
|
||||
/** @var Config|null */
|
||||
private $config = null;
|
||||
/** @var string */
|
||||
private $configFile;
|
||||
/** @var string */
|
||||
private $file;
|
||||
|
||||
/** @var PluginLogger */
|
||||
private $logger;
|
||||
|
||||
/** @var TaskScheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var ResourceProvider */
|
||||
private $resourceProvider;
|
||||
|
||||
public function __construct(PluginLoader $loader, Server $server, PluginDescription $description, string $dataFolder, string $file, ResourceProvider $resourceProvider){
|
||||
$this->loader = $loader;
|
||||
$this->server = $server;
|
||||
$this->description = $description;
|
||||
$this->dataFolder = rtrim($dataFolder, "\\/") . "/";
|
||||
//TODO: this is accessed externally via reflection, not unused
|
||||
$this->file = rtrim($file, "\\/") . "/";
|
||||
$this->configFile = $this->dataFolder . "config.yml";
|
||||
|
||||
$prefix = $this->getDescription()->getPrefix();
|
||||
$this->logger = new PluginLogger($server->getLogger(), $prefix !== "" ? $prefix : $this->getName());
|
||||
$this->scheduler = new TaskScheduler($this->getFullName());
|
||||
$this->resourceProvider = $resourceProvider;
|
||||
|
||||
$this->onLoad();
|
||||
|
||||
$this->registerYamlCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the plugin is loaded, before calling onEnable()
|
||||
*/
|
||||
protected function onLoad()/* : void /* TODO: uncomment this for next major version */{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the plugin is enabled
|
||||
*/
|
||||
protected function onEnable()/* : void /* TODO: uncomment this for next major version */{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the plugin is disabled
|
||||
* Use this to free open things and finish actions
|
||||
*/
|
||||
protected function onDisable()/* : void /* TODO: uncomment this for next major version */{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
final public function isEnabled() : bool{
|
||||
return $this->isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
|
||||
*
|
||||
* @internal This is intended for core use only and should not be used by plugins
|
||||
* @see PluginManager::enablePlugin()
|
||||
* @see PluginManager::disablePlugin()
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
final public function onEnableStateChange(bool $enabled) : void{
|
||||
if($this->isEnabled !== $enabled){
|
||||
$this->isEnabled = $enabled;
|
||||
if($this->isEnabled){
|
||||
$this->onEnable();
|
||||
}else{
|
||||
$this->onDisable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
final public function isDisabled() : bool{
|
||||
return !$this->isEnabled;
|
||||
}
|
||||
|
||||
final public function getDataFolder() : string{
|
||||
return $this->dataFolder;
|
||||
}
|
||||
|
||||
final public function getDescription() : PluginDescription{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PluginLogger
|
||||
*/
|
||||
public function getLogger() : PluginLogger{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers commands declared in the plugin manifest
|
||||
*/
|
||||
private function registerYamlCommands() : void{
|
||||
$pluginCmds = [];
|
||||
|
||||
foreach($this->getDescription()->getCommands() as $key => $data){
|
||||
if(strpos($key, ":") !== false){
|
||||
$this->logger->error($this->server->getLanguage()->translateString("pocketmine.plugin.commandError", [$key, $this->getDescription()->getFullName()]));
|
||||
continue;
|
||||
}
|
||||
if(is_array($data)){ //TODO: error out if it isn't
|
||||
$newCmd = new PluginCommand($key, $this);
|
||||
if(isset($data["description"])){
|
||||
$newCmd->setDescription($data["description"]);
|
||||
}
|
||||
|
||||
if(isset($data["usage"])){
|
||||
$newCmd->setUsage($data["usage"]);
|
||||
}
|
||||
|
||||
if(isset($data["aliases"]) and is_array($data["aliases"])){
|
||||
$aliasList = [];
|
||||
foreach($data["aliases"] as $alias){
|
||||
if(strpos($alias, ":") !== false){
|
||||
$this->logger->error($this->server->getLanguage()->translateString("pocketmine.plugin.aliasError", [$alias, $this->getDescription()->getFullName()]));
|
||||
continue;
|
||||
}
|
||||
$aliasList[] = $alias;
|
||||
}
|
||||
|
||||
$newCmd->setAliases($aliasList);
|
||||
}
|
||||
|
||||
if(isset($data["permission"])){
|
||||
if(is_bool($data["permission"])){
|
||||
$newCmd->setPermission($data["permission"] ? "true" : "false");
|
||||
}elseif(is_string($data["permission"])){
|
||||
$newCmd->setPermission($data["permission"]);
|
||||
}else{
|
||||
$this->logger->error("Permission must be a string, " . gettype($data["permission"]) . " given for command $key");
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($data["permission-message"])){
|
||||
$newCmd->setPermissionMessage($data["permission-message"]);
|
||||
}
|
||||
|
||||
$pluginCmds[] = $newCmd;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($pluginCmds) > 0){
|
||||
$this->server->getCommandMap()->registerAll($this->getDescription()->getName(), $pluginCmds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Command|PluginIdentifiableCommand|null
|
||||
*/
|
||||
public function getCommand(string $name){
|
||||
$command = $this->getServer()->getPluginCommand($name);
|
||||
if($command === null or $command->getPlugin() !== $this){
|
||||
$command = $this->getServer()->getPluginCommand(strtolower($this->description->getName()) . ":" . $name);
|
||||
}
|
||||
|
||||
if($command instanceof PluginIdentifiableCommand and $command->getPlugin() === $this){
|
||||
return $command;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommandSender $sender
|
||||
* @param Command $command
|
||||
* @param string $label
|
||||
* @param string[] $args
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an embedded resource on the plugin file.
|
||||
* WARNING: You must close the resource given using fclose()
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return null|resource Resource data, or null
|
||||
*/
|
||||
public function getResource(string $filename){
|
||||
return $this->resourceProvider->getResource($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an embedded resource to its relative location in the data folder
|
||||
*
|
||||
* @param string $filename
|
||||
* @param bool $replace
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveResource(string $filename, bool $replace = false) : bool{
|
||||
if(trim($filename) === ""){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(($resource = $this->getResource($filename)) === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$out = $this->dataFolder . $filename;
|
||||
if(!file_exists(dirname($out))){
|
||||
mkdir(dirname($out), 0755, true);
|
||||
}
|
||||
|
||||
if(file_exists($out) and !$replace){
|
||||
return false;
|
||||
}
|
||||
|
||||
$ret = stream_copy_to_stream($resource, $fp = fopen($out, "wb")) > 0;
|
||||
fclose($fp);
|
||||
fclose($resource);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the resources packaged with the plugin in the form ["path/in/resources" => SplFileInfo]
|
||||
*
|
||||
* @return \SplFileInfo[]
|
||||
*/
|
||||
public function getResources() : array{
|
||||
return $this->resourceProvider->getResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
public function getConfig() : Config{
|
||||
if($this->config === null){
|
||||
$this->reloadConfig();
|
||||
}
|
||||
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function saveConfig(){
|
||||
$this->getConfig()->save();
|
||||
}
|
||||
|
||||
public function saveDefaultConfig() : bool{
|
||||
if(!file_exists($this->configFile)){
|
||||
return $this->saveResource("config.yml", false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function reloadConfig(){
|
||||
$this->saveDefaultConfig();
|
||||
$this->config = new Config($this->configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server
|
||||
*/
|
||||
final public function getServer() : Server{
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
final public function getName() : string{
|
||||
return $this->description->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
final public function getFullName() : string{
|
||||
return $this->description->getFullName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getFile() : string{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PluginLoader
|
||||
*/
|
||||
public function getPluginLoader() : PluginLoader{
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TaskScheduler
|
||||
*/
|
||||
public function getScheduler() : TaskScheduler{
|
||||
return $this->scheduler;
|
||||
}
|
||||
}
|
313
src/plugin/PluginDescription.php
Normal file
313
src/plugin/PluginDescription.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\permission\Permission;
|
||||
use pocketmine\permission\PermissionParser;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function extension_loaded;
|
||||
use function is_array;
|
||||
use function phpversion;
|
||||
use function preg_match;
|
||||
use function str_replace;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function version_compare;
|
||||
|
||||
class PluginDescription{
|
||||
private $map;
|
||||
|
||||
private $name;
|
||||
private $main;
|
||||
private $api;
|
||||
/** @var int[] */
|
||||
private $compatibleMcpeProtocols = [];
|
||||
private $extensions = [];
|
||||
private $depend = [];
|
||||
private $softDepend = [];
|
||||
private $loadBefore = [];
|
||||
/** @var string */
|
||||
private $version;
|
||||
private $commands = [];
|
||||
/** @var string */
|
||||
private $description = "";
|
||||
/** @var string[] */
|
||||
private $authors = [];
|
||||
/** @var string */
|
||||
private $website = "";
|
||||
/** @var string */
|
||||
private $prefix = "";
|
||||
/** @var PluginLoadOrder */
|
||||
private $order;
|
||||
|
||||
/**
|
||||
* @var Permission[]
|
||||
*/
|
||||
private $permissions = [];
|
||||
|
||||
/**
|
||||
* @param string|array $yamlString
|
||||
*/
|
||||
public function __construct($yamlString){
|
||||
$this->loadMap(!is_array($yamlString) ? yaml_parse($yamlString) : $yamlString);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $plugin
|
||||
*
|
||||
* @throws PluginException
|
||||
*/
|
||||
private function loadMap(array $plugin) : void{
|
||||
$this->map = $plugin;
|
||||
|
||||
$this->name = $plugin["name"];
|
||||
if(preg_match('/^[A-Za-z0-9 _.-]+$/', $this->name) === 0){
|
||||
throw new PluginException("Invalid PluginDescription name");
|
||||
}
|
||||
$this->name = str_replace(" ", "_", $this->name);
|
||||
$this->version = (string) $plugin["version"];
|
||||
$this->main = $plugin["main"];
|
||||
if(stripos($this->main, "pocketmine\\") === 0){
|
||||
throw new PluginException("Invalid PluginDescription main, cannot start within the PocketMine namespace");
|
||||
}
|
||||
|
||||
$this->api = array_map("\strval", (array) ($plugin["api"] ?? []));
|
||||
$this->compatibleMcpeProtocols = array_map("\intval", (array) ($plugin["mcpe-protocol"] ?? []));
|
||||
|
||||
if(isset($plugin["commands"]) and is_array($plugin["commands"])){
|
||||
$this->commands = $plugin["commands"];
|
||||
}
|
||||
|
||||
if(isset($plugin["depend"])){
|
||||
$this->depend = (array) $plugin["depend"];
|
||||
}
|
||||
if(isset($plugin["extensions"])){
|
||||
$extensions = (array) $plugin["extensions"];
|
||||
$isLinear = $extensions === array_values($extensions);
|
||||
foreach($extensions as $k => $v){
|
||||
if($isLinear){
|
||||
$k = $v;
|
||||
$v = "*";
|
||||
}
|
||||
$this->extensions[$k] = is_array($v) ? $v : [$v];
|
||||
}
|
||||
}
|
||||
|
||||
$this->softDepend = (array) ($plugin["softdepend"] ?? $this->softDepend);
|
||||
|
||||
$this->loadBefore = (array) ($plugin["loadbefore"] ?? $this->loadBefore);
|
||||
|
||||
$this->website = (string) ($plugin["website"] ?? $this->website);
|
||||
|
||||
$this->description = (string) ($plugin["description"] ?? $this->description);
|
||||
|
||||
$this->prefix = (string) ($plugin["prefix"] ?? $this->prefix);
|
||||
|
||||
if(isset($plugin["load"])){
|
||||
try{
|
||||
$this->order = PluginLoadOrder::fromString($plugin["load"]);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw new PluginException("Invalid PluginDescription \"load\"");
|
||||
}
|
||||
}else{
|
||||
$this->order = PluginLoadOrder::POSTWORLD();
|
||||
}
|
||||
|
||||
$this->authors = [];
|
||||
if(isset($plugin["author"])){
|
||||
$this->authors[] = $plugin["author"];
|
||||
}
|
||||
if(isset($plugin["authors"])){
|
||||
foreach($plugin["authors"] as $author){
|
||||
$this->authors[] = $author;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($plugin["permissions"])){
|
||||
$this->permissions = PermissionParser::loadPermissions($plugin["permissions"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFullName() : string{
|
||||
return $this->name . " v" . $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCompatibleApis() : array{
|
||||
return $this->api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getCompatibleMcpeProtocols() : array{
|
||||
return $this->compatibleMcpeProtocols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAuthors() : array{
|
||||
return $this->authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix() : string{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCommands() : array{
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRequiredExtensions() : array{
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current PHP runtime has the extensions required by the plugin.
|
||||
*
|
||||
* @throws PluginException if there are required extensions missing or have incompatible version, or if the version constraint cannot be parsed
|
||||
*/
|
||||
public function checkRequiredExtensions() : void{
|
||||
foreach($this->extensions as $name => $versionConstrs){
|
||||
if(!extension_loaded($name)){
|
||||
throw new PluginException("Required extension $name not loaded");
|
||||
}
|
||||
|
||||
if(!is_array($versionConstrs)){
|
||||
$versionConstrs = [$versionConstrs];
|
||||
}
|
||||
$gotVersion = phpversion($name);
|
||||
foreach($versionConstrs as $constr){ // versionConstrs_loop
|
||||
if($constr === "*"){
|
||||
continue;
|
||||
}
|
||||
if($constr === ""){
|
||||
throw new PluginException("One of the extension version constraints of $name is empty. Consider quoting the version string in plugin.yml");
|
||||
}
|
||||
foreach(["<=", "le", "<>", "!=", "ne", "<", "lt", "==", "=", "eq", ">=", "ge", ">", "gt"] as $comparator){
|
||||
// warning: the > character should be quoted in YAML
|
||||
if(substr($constr, 0, strlen($comparator)) === $comparator){
|
||||
$version = substr($constr, strlen($comparator));
|
||||
if(!version_compare($gotVersion, $version, $comparator)){
|
||||
throw new PluginException("Required extension $name has an incompatible version ($gotVersion not $constr)");
|
||||
}
|
||||
continue 2; // versionConstrs_loop
|
||||
}
|
||||
}
|
||||
throw new PluginException("Error parsing version constraint: $constr");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDepend() : array{
|
||||
return $this->depend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription() : string{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLoadBefore() : array{
|
||||
return $this->loadBefore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMain() : string{
|
||||
return $this->main;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName() : string{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PluginLoadOrder
|
||||
*/
|
||||
public function getOrder() : PluginLoadOrder{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Permission[]
|
||||
*/
|
||||
public function getPermissions() : array{
|
||||
return $this->permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSoftDepend() : array{
|
||||
return $this->softDepend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion() : string{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getWebsite() : string{
|
||||
return $this->website;
|
||||
}
|
||||
|
||||
public function getMap() : array{
|
||||
return $this->map;
|
||||
}
|
||||
}
|
30
src/plugin/PluginException.php
Normal file
30
src/plugin/PluginException.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\utils\ServerException;
|
||||
|
||||
class PluginException extends ServerException{
|
||||
|
||||
}
|
91
src/plugin/PluginGraylist.php
Normal file
91
src/plugin/PluginGraylist.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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;
|
||||
|
||||
use Particle\Validator\Validator;
|
||||
use function array_filter;
|
||||
use function array_flip;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
class PluginGraylist{
|
||||
|
||||
/** @var string[] */
|
||||
private $plugins;
|
||||
/** @var bool */
|
||||
private $isWhitelist = false;
|
||||
|
||||
public function __construct(array $plugins = [], bool $whitelist = false){
|
||||
$this->plugins = array_flip($plugins);
|
||||
$this->isWhitelist = $whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPlugins() : array{
|
||||
return array_flip($this->plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWhitelist() : bool{
|
||||
return $this->isWhitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given name is permitted by this graylist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowed(string $name) : bool{
|
||||
return $this->isWhitelist() === isset($this->plugins[$name]);
|
||||
}
|
||||
|
||||
public static function fromArray(array $array) : PluginGraylist{
|
||||
$v = new Validator();
|
||||
$v->required("mode")->inArray(['whitelist', 'blacklist'], true);
|
||||
$v->required("plugins")->isArray()->allowEmpty(true)->callback(function(array $elements) : bool{ return count(array_filter($elements, '\is_string')) === count($elements); });
|
||||
|
||||
$result = $v->validate($array);
|
||||
if($result->isNotValid()){
|
||||
$messages = [];
|
||||
foreach($result->getFailures() as $f){
|
||||
$messages[] = $f->format();
|
||||
}
|
||||
throw new \InvalidArgumentException("Invalid data: " . implode(", ", $messages));
|
||||
}
|
||||
return new PluginGraylist($array["plugins"], $array["mode"] === 'whitelist');
|
||||
}
|
||||
|
||||
public function toArray() : array{
|
||||
return [
|
||||
"mode" => $this->isWhitelist ? 'whitelist' : 'blacklist',
|
||||
"plugins" => $this->plugins
|
||||
];
|
||||
}
|
||||
}
|
45
src/plugin/PluginLoadOrder.php
Normal file
45
src/plugin/PluginLoadOrder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\utils\EnumTrait;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
* This must be regenerated whenever enum members are added, removed or changed.
|
||||
* @see EnumTrait::_generateMethodAnnotations()
|
||||
*
|
||||
* @method static self STARTUP()
|
||||
* @method static self POSTWORLD()
|
||||
*/
|
||||
final class PluginLoadOrder{
|
||||
use EnumTrait;
|
||||
|
||||
protected static function setup() : iterable{
|
||||
return [
|
||||
new self("startup"),
|
||||
new self("postworld")
|
||||
];
|
||||
}
|
||||
}
|
62
src/plugin/PluginLoader.php
Normal file
62
src/plugin/PluginLoader.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Handles different types of plugins
|
||||
*/
|
||||
interface PluginLoader{
|
||||
|
||||
/**
|
||||
* Returns whether this PluginLoader can load the plugin in the given path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canLoadPlugin(string $path) : bool;
|
||||
|
||||
/**
|
||||
* Loads the plugin contained in $file
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public function loadPlugin(string $file) : void;
|
||||
|
||||
/**
|
||||
* Gets the PluginDescription from the file
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return null|PluginDescription
|
||||
*/
|
||||
public function getPluginDescription(string $file) : ?PluginDescription;
|
||||
|
||||
/**
|
||||
* Returns the protocol prefix used to access files in this plugin, e.g. file://, phar://
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessProtocol() : string;
|
||||
}
|
55
src/plugin/PluginLogger.php
Normal file
55
src/plugin/PluginLogger.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
class PluginLogger extends \PrefixedLogger implements \AttachableLogger{
|
||||
|
||||
/** @var \LoggerAttachment[] */
|
||||
private $attachments = [];
|
||||
|
||||
public function addAttachment(\LoggerAttachment $attachment){
|
||||
$this->attachments[spl_object_id($attachment)] = $attachment;
|
||||
}
|
||||
|
||||
public function removeAttachment(\LoggerAttachment $attachment){
|
||||
unset($this->attachments[spl_object_id($attachment)]);
|
||||
}
|
||||
|
||||
public function removeAttachments(){
|
||||
$this->attachments = [];
|
||||
}
|
||||
|
||||
public function getAttachments(){
|
||||
return $this->attachments;
|
||||
}
|
||||
|
||||
public function log($level, $message){
|
||||
parent::log($level, $message);
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->log($level, $message);
|
||||
}
|
||||
}
|
||||
}
|
604
src/plugin/PluginManager.php
Normal file
604
src/plugin/PluginManager.php
Normal file
@@ -0,0 +1,604 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\event\EventPriority;
|
||||
use pocketmine\event\HandlerList;
|
||||
use pocketmine\event\HandlerListManager;
|
||||
use pocketmine\event\Listener;
|
||||
use pocketmine\event\plugin\PluginDisableEvent;
|
||||
use pocketmine\event\plugin\PluginEnableEvent;
|
||||
use pocketmine\event\RegisteredListener;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_intersect;
|
||||
use function array_map;
|
||||
use function array_pad;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_a;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_subclass_of;
|
||||
use function iterator_to_array;
|
||||
use function mkdir;
|
||||
use function shuffle;
|
||||
use function stripos;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function strtoupper;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Manages all the plugins
|
||||
*/
|
||||
class PluginManager{
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @var Plugin[]
|
||||
*/
|
||||
protected $plugins = [];
|
||||
|
||||
/**
|
||||
* @var Plugin[]
|
||||
*/
|
||||
protected $enabledPlugins = [];
|
||||
|
||||
/**
|
||||
* @var PluginLoader[]
|
||||
*/
|
||||
protected $fileAssociations = [];
|
||||
|
||||
/** @var string|null */
|
||||
private $pluginDataDirectory;
|
||||
/** @var PluginGraylist|null */
|
||||
private $graylist;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @param null|string $pluginDataDirectory
|
||||
* @param PluginGraylist|null $graylist
|
||||
*/
|
||||
public function __construct(Server $server, ?string $pluginDataDirectory, ?PluginGraylist $graylist = null){
|
||||
$this->server = $server;
|
||||
$this->pluginDataDirectory = $pluginDataDirectory;
|
||||
if($this->pluginDataDirectory !== null){
|
||||
if(!file_exists($this->pluginDataDirectory)){
|
||||
@mkdir($this->pluginDataDirectory, 0777, true);
|
||||
}elseif(!is_dir($this->pluginDataDirectory)){
|
||||
throw new \RuntimeException("Plugin data path $this->pluginDataDirectory exists and is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
$this->graylist = $graylist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return null|Plugin
|
||||
*/
|
||||
public function getPlugin(string $name) : ?Plugin{
|
||||
if(isset($this->plugins[$name])){
|
||||
return $this->plugins[$name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PluginLoader $loader
|
||||
*/
|
||||
public function registerInterface(PluginLoader $loader) : void{
|
||||
$this->fileAssociations[get_class($loader)] = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin[]
|
||||
*/
|
||||
public function getPlugins() : array{
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
private function getDataDirectory(string $pluginPath, string $pluginName) : string{
|
||||
if($this->pluginDataDirectory !== null){
|
||||
return $this->pluginDataDirectory . $pluginName;
|
||||
}
|
||||
return dirname($pluginPath) . DIRECTORY_SEPARATOR . $pluginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param PluginLoader[] $loaders
|
||||
*
|
||||
* @return Plugin|null
|
||||
*/
|
||||
public function loadPlugin(string $path, ?array $loaders = null) : ?Plugin{
|
||||
foreach($loaders ?? $this->fileAssociations as $loader){
|
||||
if($loader->canLoadPlugin($path)){
|
||||
$description = $loader->getPluginDescription($path);
|
||||
if($description instanceof PluginDescription){
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.load", [$description->getFullName()]));
|
||||
try{
|
||||
$description->checkRequiredExtensions();
|
||||
}catch(PluginException $ex){
|
||||
$this->server->getLogger()->error($ex->getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
$dataFolder = $this->getDataDirectory($path, $description->getName());
|
||||
if(file_exists($dataFolder) and !is_dir($dataFolder)){
|
||||
$this->server->getLogger()->error("Projected dataFolder '" . $dataFolder . "' for " . $description->getName() . " exists and is not a directory");
|
||||
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("Main class for plugin " . $description->getName() . " not found");
|
||||
return null;
|
||||
}
|
||||
if(!is_a($mainClass, Plugin::class, true)){
|
||||
$this->server->getLogger()->error("Main class for plugin " . $description->getName() . " is not an instance of " . Plugin::class);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Plugin $plugin
|
||||
* @see Plugin::__construct()
|
||||
*/
|
||||
$plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, new DiskResourceProvider($prefixed . "/"));
|
||||
$this->plugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param array $newLoaders
|
||||
*
|
||||
* @return Plugin[]
|
||||
*/
|
||||
public function loadPlugins(string $directory, ?array $newLoaders = null) : array{
|
||||
if(!is_dir($directory)){
|
||||
return [];
|
||||
}
|
||||
|
||||
$plugins = [];
|
||||
$loadedPlugins = [];
|
||||
$dependencies = [];
|
||||
$softDependencies = [];
|
||||
if(is_array($newLoaders)){
|
||||
$loaders = [];
|
||||
foreach($newLoaders as $key){
|
||||
if(isset($this->fileAssociations[$key])){
|
||||
$loaders[$key] = $this->fileAssociations[$key];
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$loaders = $this->fileAssociations;
|
||||
}
|
||||
|
||||
$files = iterator_to_array(new \FilesystemIterator($directory, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
|
||||
shuffle($files); //this prevents plugins implicitly relying on the filesystem name order when they should be using dependency properties
|
||||
foreach($loaders as $loader){
|
||||
foreach($files as $file){
|
||||
if(!$loader->canLoadPlugin($file)){
|
||||
continue;
|
||||
}
|
||||
try{
|
||||
$description = $loader->getPluginDescription($file);
|
||||
}catch(\RuntimeException $e){ //TODO: more specific exception handling
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.fileError", [$file, $directory, $e->getMessage()]));
|
||||
$this->server->getLogger()->logException($e);
|
||||
continue;
|
||||
}
|
||||
if($description === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $description->getName();
|
||||
if(stripos($name, "pocketmine") !== false or stripos($name, "minecraft") !== false or stripos($name, "mojang") !== false){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.restrictedName"]));
|
||||
continue;
|
||||
}
|
||||
if(strpos($name, " ") !== false){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.spacesDiscouraged", [$name]));
|
||||
}
|
||||
|
||||
if(isset($plugins[$name]) or $this->getPlugin($name) instanceof Plugin){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.duplicateError", [$name]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!ApiVersion::isCompatible($this->server->getApiVersion(), $description->getCompatibleApis())){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [
|
||||
$name,
|
||||
$this->server->getLanguage()->translateString("%pocketmine.plugin.incompatibleAPI", [implode(", ", $description->getCompatibleApis())])
|
||||
]));
|
||||
continue;
|
||||
}
|
||||
$ambiguousVersions = ApiVersion::checkAmbiguousVersions($description->getCompatibleApis());
|
||||
if(!empty($ambiguousVersions)){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [
|
||||
$name,
|
||||
$this->server->getLanguage()->translateString("pocketmine.plugin.ambiguousMinAPI", [implode(", ", $ambiguousVersions)])
|
||||
]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count($pluginMcpeProtocols = $description->getCompatibleMcpeProtocols()) > 0){
|
||||
$serverMcpeProtocols = [ProtocolInfo::CURRENT_PROTOCOL];
|
||||
if(count(array_intersect($pluginMcpeProtocols, $serverMcpeProtocols)) === 0){
|
||||
$this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [
|
||||
$name,
|
||||
$this->server->getLanguage()->translateString("%pocketmine.plugin.incompatibleProtocol", [implode(", ", $pluginMcpeProtocols)])
|
||||
]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->graylist !== null and !$this->graylist->isAllowed($name)){
|
||||
$this->server->getLogger()->notice($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [
|
||||
$name,
|
||||
"Disallowed by graylist"
|
||||
]));
|
||||
continue;
|
||||
}
|
||||
$plugins[$name] = $file;
|
||||
|
||||
$softDependencies[$name] = array_merge($softDependencies[$name] ?? [], $description->getSoftDepend());
|
||||
$dependencies[$name] = $description->getDepend();
|
||||
|
||||
foreach($description->getLoadBefore() as $before){
|
||||
if(isset($softDependencies[$before])){
|
||||
$softDependencies[$before][] = $name;
|
||||
}else{
|
||||
$softDependencies[$before] = [$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while(count($plugins) > 0){
|
||||
$missingDependency = true;
|
||||
foreach($plugins as $name => $file){
|
||||
if(isset($dependencies[$name])){
|
||||
foreach($dependencies[$name] as $key => $dependency){
|
||||
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()->translateString("pocketmine.plugin.loadError", [
|
||||
$name,
|
||||
$this->server->getLanguage()->translateString("%pocketmine.plugin.unknownDependency", [$dependency])
|
||||
]));
|
||||
unset($plugins[$name]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($dependencies[$name]) === 0){
|
||||
unset($dependencies[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($softDependencies[$name])){
|
||||
foreach($softDependencies[$name] as $key => $dependency){
|
||||
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
|
||||
unset($softDependencies[$name][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($softDependencies[$name]) === 0){
|
||||
unset($softDependencies[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){
|
||||
unset($plugins[$name]);
|
||||
$missingDependency = false;
|
||||
if($plugin = $this->loadPlugin($file, $loaders) and $plugin instanceof Plugin){
|
||||
$loadedPlugins[$name] = $plugin;
|
||||
}else{
|
||||
$this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.genericLoadError", [$name]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($missingDependency){
|
||||
foreach($plugins as $name => $file){
|
||||
if(!isset($dependencies[$name])){
|
||||
unset($softDependencies[$name]);
|
||||
unset($plugins[$name]);
|
||||
$missingDependency = false;
|
||||
if($plugin = $this->loadPlugin($file, $loaders) and $plugin instanceof Plugin){
|
||||
$loadedPlugins[$name] = $plugin;
|
||||
}else{
|
||||
$this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.genericLoadError", [$name]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//No plugins loaded :(
|
||||
if($missingDependency){
|
||||
foreach($plugins as $name => $file){
|
||||
$this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.circularDependency"]));
|
||||
}
|
||||
$plugins = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $loadedPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a specified API version string is considered compatible with the server's API version.
|
||||
*
|
||||
* @param string ...$versions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCompatibleApi(string ...$versions) : bool{
|
||||
$serverString = $this->server->getApiVersion();
|
||||
$serverApi = array_pad(explode("-", $serverString, 2), 2, "");
|
||||
$serverNumbers = array_map("\intval", explode(".", $serverApi[0]));
|
||||
|
||||
foreach($versions as $version){
|
||||
//Format: majorVersion.minorVersion.patch (3.0.0)
|
||||
// or: majorVersion.minorVersion.patch-devBuild (3.0.0-alpha1)
|
||||
if($version !== $serverString){
|
||||
$pluginApi = array_pad(explode("-", $version, 2), 2, ""); //0 = version, 1 = suffix (optional)
|
||||
|
||||
if(strtoupper($pluginApi[1]) !== strtoupper($serverApi[1])){ //Different release phase (alpha vs. beta) or phase build (alpha.1 vs alpha.2)
|
||||
continue;
|
||||
}
|
||||
|
||||
$pluginNumbers = array_map("\intval", array_pad(explode(".", $pluginApi[0]), 3, "0")); //plugins might specify API like "3.0" or "3"
|
||||
|
||||
if($pluginNumbers[0] !== $serverNumbers[0]){ //Completely different API version
|
||||
continue;
|
||||
}
|
||||
|
||||
if($pluginNumbers[1] > $serverNumbers[1]){ //If the plugin requires new API features, being backwards compatible
|
||||
continue;
|
||||
}
|
||||
|
||||
if($pluginNumbers[1] === $serverNumbers[1] and $pluginNumbers[2] > $serverNumbers[2]){ //If the plugin requires bug fixes in patches, being backwards compatible
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Plugin $plugin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPluginEnabled(Plugin $plugin) : bool{
|
||||
return isset($this->plugins[$plugin->getDescription()->getName()]) and $plugin->isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Plugin $plugin
|
||||
*/
|
||||
public function enablePlugin(Plugin $plugin) : void{
|
||||
if(!$plugin->isEnabled()){
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.enable", [$plugin->getDescription()->getFullName()]));
|
||||
|
||||
$permManager = PermissionManager::getInstance();
|
||||
foreach($plugin->getDescription()->getPermissions() as $perm){
|
||||
$permManager->addPermission($perm);
|
||||
}
|
||||
$plugin->getScheduler()->setEnabled(true);
|
||||
$plugin->onEnableStateChange(true);
|
||||
|
||||
$this->enabledPlugins[$plugin->getDescription()->getName()] = $plugin;
|
||||
|
||||
(new PluginEnableEvent($plugin))->call();
|
||||
}
|
||||
}
|
||||
|
||||
public function disablePlugins() : void{
|
||||
foreach($this->getPlugins() as $plugin){
|
||||
$this->disablePlugin($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Plugin $plugin
|
||||
*/
|
||||
public function disablePlugin(Plugin $plugin) : void{
|
||||
if($plugin->isEnabled()){
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.disable", [$plugin->getDescription()->getFullName()]));
|
||||
(new PluginDisableEvent($plugin))->call();
|
||||
|
||||
unset($this->enabledPlugins[$plugin->getDescription()->getName()]);
|
||||
|
||||
$plugin->onEnableStateChange(false);
|
||||
$plugin->getScheduler()->shutdown();
|
||||
HandlerListManager::global()->unregisterAll($plugin);
|
||||
$permManager = PermissionManager::getInstance();
|
||||
foreach($plugin->getDescription()->getPermissions() as $perm){
|
||||
$permManager->removePermission($perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function tickSchedulers(int $currentTick) : void{
|
||||
foreach($this->enabledPlugins as $p){
|
||||
$p->getScheduler()->mainThreadHeartbeat($currentTick);
|
||||
}
|
||||
}
|
||||
|
||||
public function clearPlugins() : void{
|
||||
$this->disablePlugins();
|
||||
$this->plugins = [];
|
||||
$this->enabledPlugins = [];
|
||||
$this->fileAssociations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all the events in the given Listener class
|
||||
*
|
||||
* @param Listener $listener
|
||||
* @param Plugin $plugin
|
||||
*
|
||||
* @throws PluginException
|
||||
*/
|
||||
public function registerEvents(Listener $listener, Plugin $plugin) : void{
|
||||
if(!$plugin->isEnabled()){
|
||||
throw new PluginException("Plugin attempted to register " . get_class($listener) . " while not enabled");
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(get_class($listener));
|
||||
foreach($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method){
|
||||
if(!$method->isStatic() and $method->getDeclaringClass()->implementsInterface(Listener::class)){
|
||||
$tags = Utils::parseDocComment((string) $method->getDocComment());
|
||||
if(isset($tags["notHandler"])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameters = $method->getParameters();
|
||||
if(count($parameters) !== 1){
|
||||
continue;
|
||||
}
|
||||
|
||||
$handlerClosure = $method->getClosure($listener);
|
||||
|
||||
try{
|
||||
$eventClass = $parameters[0]->getClass();
|
||||
}catch(\ReflectionException $e){ //class doesn't exist
|
||||
if(isset($tags["softDepend"]) && !isset($this->plugins[$tags["softDepend"]])){
|
||||
$this->server->getLogger()->debug("Not registering @softDepend listener " . Utils::getNiceClosureName($handlerClosure) . "(" . $parameters[0]->getType()->getName() . ") because plugin \"" . $tags["softDepend"] . "\" not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
if($eventClass === null or !$eventClass->isSubclassOf(Event::class)){
|
||||
continue;
|
||||
}
|
||||
|
||||
try{
|
||||
$priority = isset($tags["priority"]) ? EventPriority::fromString($tags["priority"]) : EventPriority::NORMAL;
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw new PluginException("Event handler " . Utils::getNiceClosureName($handlerClosure) . "() declares invalid/unknown priority \"" . $tags["priority"] . "\"");
|
||||
}
|
||||
|
||||
$handleCancelled = false;
|
||||
if(isset($tags["handleCancelled"])){
|
||||
switch(strtolower($tags["handleCancelled"])){
|
||||
case "true":
|
||||
case "":
|
||||
$handleCancelled = true;
|
||||
break;
|
||||
case "false":
|
||||
break;
|
||||
default:
|
||||
throw new PluginException("Event handler " . Utils::getNiceClosureName($handlerClosure) . "() declares invalid @handleCancelled value \"" . $tags["handleCancelled"] . "\"");
|
||||
}
|
||||
}
|
||||
|
||||
$this->registerEvent($eventClass->getName(), $handlerClosure, $priority, $plugin, $handleCancelled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $event Class name that extends Event
|
||||
* @param \Closure $handler
|
||||
* @param int $priority
|
||||
* @param Plugin $plugin
|
||||
* @param bool $handleCancelled
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function registerEvent(string $event, \Closure $handler, int $priority, Plugin $plugin, bool $handleCancelled = false) : void{
|
||||
if(!is_subclass_of($event, Event::class)){
|
||||
throw new PluginException($event . " is not an Event");
|
||||
}
|
||||
|
||||
$handlerName = Utils::getNiceClosureName($handler);
|
||||
|
||||
$tags = Utils::parseDocComment((string) (new \ReflectionClass($event))->getDocComment());
|
||||
if(isset($tags["deprecated"]) and $this->server->getProperty("settings.deprecated-verbose", true)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.deprecatedEvent", [
|
||||
$plugin->getName(),
|
||||
$event,
|
||||
$handlerName
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
if(!$plugin->isEnabled()){
|
||||
throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled");
|
||||
}
|
||||
|
||||
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . $handlerName . "(" . (new \ReflectionClass($event))->getShortName() . ")");
|
||||
|
||||
$this->getEventListeners($event)->register(new RegisteredListener($handler, $priority, $plugin, $handleCancelled, $timings));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $event
|
||||
*
|
||||
* @return HandlerList
|
||||
*/
|
||||
private function getEventListeners(string $event) : HandlerList{
|
||||
$list = HandlerListManager::global()->getListFor($event);
|
||||
if($list === null){
|
||||
throw new PluginException("Abstract events not declaring @allowHandle cannot be handled (tried to register listener for $event)");
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
43
src/plugin/ResourceProvider.php
Normal file
43
src/plugin/ResourceProvider.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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;
|
||||
|
||||
interface ResourceProvider{
|
||||
/**
|
||||
* Gets an embedded resource on the plugin file.
|
||||
* WARNING: You must close the resource given using fclose()
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return null|resource Resource data, or null
|
||||
*/
|
||||
public function getResource(string $filename);
|
||||
|
||||
/**
|
||||
* Returns all the resources packaged with the plugin in the form ["path/in/resources" => SplFileInfo]
|
||||
*
|
||||
* @return \SplFileInfo[]
|
||||
*/
|
||||
public function getResources() : array;
|
||||
}
|
99
src/plugin/ScriptPluginLoader.php
Normal file
99
src/plugin/ScriptPluginLoader.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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;
|
||||
|
||||
use function file;
|
||||
use function is_file;
|
||||
use function preg_match;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use const FILE_IGNORE_NEW_LINES;
|
||||
use const FILE_SKIP_EMPTY_LINES;
|
||||
|
||||
/**
|
||||
* Simple script loader, not for plugin development
|
||||
* For an example see https://gist.github.com/shoghicp/516105d470cf7d140757
|
||||
*/
|
||||
class ScriptPluginLoader implements PluginLoader{
|
||||
|
||||
public function canLoadPlugin(string $path) : bool{
|
||||
$ext = ".php";
|
||||
return is_file($path) and substr($path, -strlen($ext)) === $ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the plugin contained in $file
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public function loadPlugin(string $file) : void{
|
||||
include_once $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PluginDescription from the file
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return null|PluginDescription
|
||||
*/
|
||||
public function getPluginDescription(string $file) : ?PluginDescription{
|
||||
$content = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
$data = [];
|
||||
|
||||
$insideHeader = false;
|
||||
foreach($content as $line){
|
||||
if(!$insideHeader and strpos($line, "/**") !== false){
|
||||
$insideHeader = true;
|
||||
}
|
||||
|
||||
if(preg_match("/^[ \t]+\\*[ \t]+@([a-zA-Z]+)([ \t]+(.*))?$/", $line, $matches) > 0){
|
||||
$key = $matches[1];
|
||||
$content = trim($matches[3] ?? "");
|
||||
|
||||
if($key === "notscript"){
|
||||
return null;
|
||||
}
|
||||
|
||||
$data[$key] = $content;
|
||||
}
|
||||
|
||||
if($insideHeader and strpos($line, "*/") !== false){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($insideHeader){
|
||||
return new PluginDescription($data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAccessProtocol() : string{
|
||||
return "";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user