server = ServerAPI::request(); $this->randomNonce = Utils::getRandomBytes(16, false); $this->server->api->console->register("plugins", "", array($this, "commandHandler")); $this->server->api->console->register("version", "", array($this, "commandHandler")); $this->server->api->ban->cmdWhitelist("version"); } public function commandHandler($cmd, $params, $issuer, $alias){ $output = ""; switch($cmd){ case "plugins": $output = "Plugins: "; foreach($this->getList() as $plugin){ $output .= $plugin["name"] . ": ".$plugin["version"] .", "; } $output = $output === "Plugins: " ? "No plugins installed.\n" : substr($output, 0, -2)."\n"; break; case "version": $output = "PocketMine-MP ".MAJOR_VERSION." 「".CODENAME."」 API #".CURRENT_API_VERSION." for Minecraft: PE ".CURRENT_MINECRAFT_VERSION." protocol #".ProtocolInfo::CURRENT_PROTOCOL; if(GIT_COMMIT !== str_repeat("00", 20)){ $output .= " (git ".GIT_COMMIT.")"; } $output .= "\n"; break; } return $output; } public function __destruct(){ foreach($this->plugins as $p){ $p[0]->__destruct(); } unset($plugins); } public function getList(){ $list = array(); foreach($this->plugins as $p){ $list[] = $p[1]; } return $list; } public function getAll(){ return $this->plugins; } public function load($file){ if(is_link($file) or is_dir($file) or !file_exists($file)){ console("[ERROR] ".basename($file)." is not a file"); return false; } if(strtolower(substr($file, -3)) === "pmf"){ $pmf = new PMFPlugin($file); $info = $pmf->getPluginInfo(); }else{ $content = file_get_contents($file); $info = strstr($content, "*/", true); $content = str_repeat(PHP_EOL, substr_count($info, "\n")).substr(strstr($content, "*/"),2); if(preg_match_all('#([a-zA-Z0-9\-_]*)=([^\r\n]*)#u', $info, $matches) == 0){ //false or 0 matches console("[ERROR] Failed parsing of ".basename($file)); return false; } $info = array(); foreach($matches[1] as $k => $i){ $v = $matches[2][$k]; switch(strtolower($v)){ case "on": case "true": case "yes": $v = true; break; case "off": case "false": case "no": $v = false; break; } $info[$i] = $v; } $info["code"] = $content; $info["class"] = trim(strtolower($info["class"])); } if(!isset($info["name"]) or !isset($info["version"]) or !isset($info["class"]) or !isset($info["author"])){ console("[ERROR] Failed parsing of ".basename($file)); return false; } console("[INFO] Loading plugin \"".TextFormat::GREEN.$info["name"].TextFormat::RESET."\" ".TextFormat::AQUA.$info["version"].TextFormat::RESET." by ".TextFormat::AQUA.$info["author"].TextFormat::RESET); if($info["class"] !== "none" and class_exists($info["class"])){ console("[ERROR] Failed loading plugin: class already exists"); return false; } if(((!isset($pmf) and (include $file) === false) or (isset($pmf) and eval($info["code"]) === false)) and $info["class"] !== "none" and !class_exists($info["class"])){ console("[ERROR] Failed loading {$info['name']}: evaluation error"); return false; } $className = $info["class"]; $apiversion = array_map("intval", explode(",", (string) $info["apiversion"])); if(!in_array((string) CURRENT_API_VERSION, $apiversion)){ console("[WARNING] Plugin \"".$info["name"]."\" may not be compatible with the API (".$info["apiversion"]." != ".CURRENT_API_VERSION.")! It can crash or corrupt the server!"); } $identifier = $this->getIdentifier($info["name"], $info["author"]); if($info["class"] !== "none"){ $object = new $className($this->server->api, false); if(!($object instanceof Plugin)){ console("[ERROR] Plugin \"".$info["name"]."\" doesn't use the Plugin Interface"); if(method_exists($object, "__destruct")){ $object->__destruct(); } $object = null; unset($object); }else{ $this->plugins[$identifier] = array($object, $info); } }else{ $this->plugins[$identifier] = array(new DummyPlugin($this->server->api, false), $info); } return true; } public function getIdentifier($name, $author){ return sha1(trim(strtolower($name)), true) ^ sha1(trim(strtolower($author)), true) ^ sha1($this->randomNonce, true); } public function get($identifier){ if($identifier instanceof Plugin){ foreach($this->plugins as $p){ if($p[0] === $identifier){ return $p; } } return false; } if(isset($this->plugins[$identifier])){ return $this->plugins[$identifier]; } return false; } public function pluginsPath(){ $path = join(DIRECTORY_SEPARATOR, array(DATA_PATH."plugins", "")); @mkdir($path); return $path; } public function configPath(Plugin $plugin){ $p = $this->get($plugin); $identifier = $this->getIdentifier($p[1]["name"], $p[1]["author"]); if($p === false){ return false; } $path = $this->pluginsPath() . $p[1]["name"] . DIRECTORY_SEPARATOR; $this->plugins[$identifier][1]["path"] = $path; @mkdir($path); return $path; } public function createConfig(Plugin $plugin, $default = array()){ $p = $this->get($plugin); if($p === false){ return false; } $path = $this->configPath($plugin); $cnf = new Config($path."config.yml", Config::YAML, $default); $cnf->save(); return $path; } public function readYAML($file){ return yaml_parse(preg_replace("#^([ ]*)([a-zA-Z_]{1}[^\:]*)\:#m", "$1\"$2\":", file_get_contents($file))); } public function writeYAML($file, $data){ return file_put_contents($file, yaml_emit($data, YAML_UTF8_ENCODING)); } public function init(){ $this->server->event("server.start", array($this, "initAll")); $this->loadAll(); } private function loadAll(){ $dir = dir($this->pluginsPath()); while(false !== ($file = $dir->read())){ if($file{0} !== "."){ $ext = strtolower(substr($file, -3)); if($ext === "php" or $ext === "pmf"){ $this->load($dir->path . $file); } } } } public function initAll(){ console("[INFO] Starting plugins..."); foreach($this->plugins as $p){ $p[0]->init(); //ARGHHH!!! Plugin loading randomly fails!! } } } interface Plugin{ public function __construct(ServerAPI $api, $server = false); public function init(); public function __destruct(); } class DummyPlugin implements Plugin{ public function __construct(ServerAPI $api, $server = false){ } public function init(){ } public function __destruct(){ } }