Removed pocketmine subdirectory, map PSR-4 style

This commit is contained in:
Dylan K. Taylor
2019-07-30 19:14:57 +01:00
parent 7a77d3dc30
commit 5499ac620c
1044 changed files with 3 additions and 3 deletions

103
src/plugin/ApiVersion.php Normal file
View 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);
}
}

View 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;
}
}

View 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
View 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
View 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;
}
}

View 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;
}
}

View 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{
}

View 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
];
}
}

View 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")
];
}
}

View 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;
}

View 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);
}
}
}

View 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;
}
}

View 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;
}

View 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 "";
}
}