diff --git a/README.md b/README.md index 9e214a6..c4feb88 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ # ManyWorlds -* Summary: Full Suite for MultiWorld functionality +* Summary: Manage Multiple Worlds * Dependency Plugins: n/a -* PocketMine-MP version: 1.4 - API 1.10.0 +* PocketMine-MP version: 1.4 (API:1.10.0) * OptionalPlugins: n/a -* Categories: World Editing and Management, Teleportation +* Categories: Admin Tools, Teleportation * Plugin Access: Commands, Manages Worlds * WebSite: [github](https://github.com/alejandroliu/pocketmine-plugins/tree/master/ManyWorlds) @@ -166,12 +166,34 @@ for sample files. * A: You must be using PocketMine-MP v1.4.1. Set the `generator` to `flat`. * Q: How do I load multiple worlds on start-up? - A: That functionality is provided by PocketMine-MP core by default. +* A: That functionality is provided by PocketMine-MP core by default. In the `pocketmine.yml` file there is a `worlds` section where you - can define which worlds to load on start-up. + can define which worlds to load on start-up. Examples: + + [CODE] + + # pocketmine.yml + worlds: + world1: [] + world2: [] + [/CODE] + + This will automatically load worlds: "world1" and "world2" on startup. # Changes +* 2.0.3: Bugfix update + * Fixes bug reported by @thebigsmileXD +* 2.0.2: Bug fix + * Updated libcommon to 1.2.0dev1 + * This fixes a bug reported by @SoyPro. (#23) + * Note this means that permissions defined in `plugin.yml` are applied + properly which means all **ManyWorlds** sub commands are **OP only**. +* 2.0.1: Bug fix + * Changed command to manyworlds and mw is an alias. This is to + prevent possible name collisions. + * Completed Spanish translation. + * Fixed crash (reported by @reyak) * 2.0.0: Modularization * Re-written for modularity * teleport manager API deprecated diff --git a/plugin.yml b/plugin.yml index c9cf3ba..ac85287 100644 --- a/plugin.yml +++ b/plugin.yml @@ -4,14 +4,15 @@ load: POSTWORLD name: ManyWorlds description: Manage Multiple Worlds -version: 2.0.0 +version: 2.0.3 author: aliuly commands: - mw: + manyworlds: description: Manage worlds usage: "/mw [options]" permission: mw.cmds + aliases: [mw] permissions: mw.cmds: diff --git a/resources/messages/messages.ini b/resources/messages/messages.ini index 45c2595..ff7b08f 100644 --- a/resources/messages/messages.ini +++ b/resources/messages/messages.ini @@ -1,6 +1,7 @@ ; messages.ini "%1%-mode"="" "%1% is already in %2%"="" +"%1% not found"="" "- /mw unload --enable"="" ""="" " [attr=value]"="" @@ -18,7 +19,7 @@ "Creative"="" "Default world changed to %1%"="" "Description: "="" -"Enable %1% features"="" +"Enabled %1% features"="" "Enabled one feature"="" "Error GetLevelByName %1%"="" "Error getting %1%"="" @@ -39,6 +40,7 @@ "Name unchanged"="" "No change"="" "No help for %1%"="" +"No sub-commands available"="" "No sub-command specified"="" "Nothing happens"="" "Path: "="" @@ -104,6 +106,5 @@ "loaded"="" "players:%1%"="" "to fix this issue"="" -"# [player]"="" -"#List world ifnormation"="" -"#Restart server for changes to take effect!"="" +"mc.lang"="C" +"mc.version"="2.0" diff --git a/resources/messages/spa.ini b/resources/messages/spa.ini index 542e030..fb8a65d 100644 --- a/resources/messages/spa.ini +++ b/resources/messages/spa.ini @@ -1,12 +1,13 @@ ; spa.ini "%1%-mode"="modo-%1%" "%1% is already in %2%"="%1% ya está en %2%" -"- /mw unload --enable"="" +"%1% not found"="" +"- /mw unload --enable"="- /mw unload --enable" ""="" " [attr=value]"=" [attr=valor]" " [seed] [generator] [preset]"=" [semilla] [generador] [preset]" ""="" -"> /mw fixname %1%"="" +"> /mw fixname %1%"="> /mw fixname %1%" "Adventure"="Aventura" "Attempt to unload worlds"="Intentando descargar mundos" "Available sub-commands for %1%"="Sub-commandos disponibles para %1%" @@ -18,7 +19,7 @@ "Creative"="Creativo" "Default world changed to %1%"="Mundo inicial es ahora %1% " "Description: "="Descripción" -"Enable %1% features"="Activando %1% módulos" +"Enabled %1% features"="Activando %1% módulos" "Enabled one feature"="Activando un módulo" "Error GetLevelByName %1%"="Error de GetLevelByName %1%" "Error getting %1%"="Error obteniendo %1%" @@ -39,6 +40,7 @@ "Name unchanged"="Nombre no ha cambiado" "No change"="No hay cambios" "No help for %1%"="No hay ayuda para %1%" +"No sub-commands available"="No sub-comandos disponibles" "No sub-command specified"="No sub-comando especificado" "Nothing happens"="No pasa nada" "Path: "="Ruta: " @@ -104,4 +106,7 @@ "loaded"="cargado" "players:%1%"="jugadores:%1%" "to fix this issue"="para corrregir este problema" +"mc.lang"="spa" +"mc.version"="2.0" +"#Enable %1% features"="Activando %1% módulos" "#Restart server for changes to take effect!"="Re-inicia el servidor para que los cambios se apliquen" diff --git a/src/aliuly/manyworlds/Main.php b/src/aliuly/manyworlds/Main.php index a097172..6b2d3f4 100644 --- a/src/aliuly/manyworlds/Main.php +++ b/src/aliuly/manyworlds/Main.php @@ -73,7 +73,7 @@ public function autoLoad(CommandSender $c,$world) { // ////////////////////////////////////////////////////////////////////// public function onCommand(CommandSender $sender, Command $cmd, $label, array $args) { - if ($cmd->getName() != "mw") return false; + if ($cmd->getName() != "manyworlds") return false; return $this->dispatchSCmd($sender,$cmd,$args); } // diff --git a/src/aliuly/manyworlds/MwLvDat.php b/src/aliuly/manyworlds/MwLvDat.php index ab2175b..62d95bd 100644 --- a/src/aliuly/manyworlds/MwLvDat.php +++ b/src/aliuly/manyworlds/MwLvDat.php @@ -37,6 +37,7 @@ use pocketmine\nbt\tag\String; use pocketmine\nbt\tag\Long; use pocketmine\nbt\tag\Compound; +use pocketmine\math\Vector3; class MwLvDat extends BasicCli { public function __construct($owner) { @@ -59,7 +60,7 @@ public function onSCommand(CommandSender $c,Command $cc,$scmd,$data,array $args) } $world = array_shift($args); if(!$this->owner->autoLoad($c,$world)) { - $c->sendMessage(TextFormat::RED.mc::_("[MW] %1% is not loaded!",$worl)); + $c->sendMessage(TextFormat::RED.mc::_("[MW] %1% is not loaded!",$world)); return true; } $level = $this->owner->getServer()->getLevelByName($world); diff --git a/src/aliuly/manyworlds/common/BasicCli.php b/src/aliuly/manyworlds/common/BasicCli.php index 40878e1..2ed49c6 100644 --- a/src/aliuly/manyworlds/common/BasicCli.php +++ b/src/aliuly/manyworlds/common/BasicCli.php @@ -1,4 +1,8 @@ owner = $owner; } - + /** + * Register this class as a sub-command. See BasicPlugin for details. + * + * @param str $cmd - sub-command to register + * @param mixed[] $opts - additional options for registering sub-command + */ public function enableSCmd($cmd,$opts) { $this->owner->registerScmd($cmd,[$this,"onSCommand"],$opts); } + /** + * Register this class as a command. + * + * @param str $cmd - command to register + * @param mixed[] $yaml - options for command + */ public function enableCmd($cmd,$yaml) { $newCmd = new PluginCommand($cmd,$this->owner); if (isset($yaml["description"])) @@ -43,8 +65,13 @@ public function enableCmd($cmd,$yaml) { $cmdMap = $this->owner->getServer()->getCommandMap(); $cmdMap->register($this->owner->getDescription()->getName(),$newCmd); } - - // Paginate output + /** + * Use for paginaged output implementation. + * This gets the player specified page number that we want to Display + * + * @param str[] $args - Passed arguments + * @return int page number + */ protected function getPageNumber(array &$args) { $pageNumber = 1; if (count($args) && is_numeric($args[count($args)-1])) { @@ -53,6 +80,15 @@ protected function getPageNumber(array &$args) { } return $pageNumber; } + /** + * Use for paginaged output implementation. + * Shows a bunch of line in paginated output. + * + * @param CommandSender $sender - entity that we need to display text to + * @param int $pageNumber - page that we need to display + * @param str[] $txt - Array containing one element per output line + * @return bool true + */ protected function paginateText(CommandSender $sender,$pageNumber,array $txt) { $hdr = array_shift($txt); if($sender instanceof ConsoleCommandSender){ @@ -76,6 +112,15 @@ protected function paginateText(CommandSender $sender,$pageNumber,array $txt) { } return true; } + /** + * Use for paginaged output implementation. + * Formats and paginates a table + * + * @param CommandSender $sender - entity that we need to display text to + * @param int $pageNumber - page that we need to display + * @param str[][] $txt - Array containing one element per cell + * @return bool true + */ protected function paginateTable(CommandSender $sender,$pageNumber,array $tab) { $cols = []; for($i=0;$i < count($tab[0]);$i++) $cols[$i] = strlen($tab[0][$i]); @@ -97,12 +142,36 @@ protected function paginateTable(CommandSender $sender,$pageNumber,array $tab) { } ////////////////////////////////////////////////////////////////////// + /** + * Entry point for BasicPlugin state functionality. This makes it module + * specific. + * Retrieves the state. + * + * @param CommandSender $player - entity that we need state from + * @param mixed $default - Default value to return if no state found + * @return mixed $state + */ public function getState(CommandSender $player,$default) { return $this->owner->getState(get_class($this),$player,$default); } + /** + * Entry point for BasicPlugin state functionality. This makes it module + * specific. + * Sets the state. + * + * @param CommandSender $player - entity that we need to set state + * @param mixed $val - Value to use for the state + */ public function setState(CommandSender $player,$val) { $this->owner->setState(get_class($this),$player,$val); } + /** + * Entry point for BasicPlugin state functionality. This makes it module + * specific. + * UnSets the state. + * + * @param CommandSender $player - entity that we need to unset state + */ public function unsetState(CommandSender $player) { $this->owner->unsetState(get_class($this),$player); } diff --git a/src/aliuly/manyworlds/common/BasicHelp.php b/src/aliuly/manyworlds/common/BasicHelp.php index 5911b40..2042b49 100644 --- a/src/aliuly/manyworlds/common/BasicHelp.php +++ b/src/aliuly/manyworlds/common/BasicHelp.php @@ -5,55 +5,69 @@ use pocketmine\command\Command; use pocketmine\utils\TextFormat; use aliuly\manyworlds\common\mc; -use aliuly\manyworlds\common\Cli; use aliuly\manyworlds\common\BasicCli; +/** + * Implements simple help functionality for sub-commands + */ class BasicHelp extends BasicCli { protected $fmt; + /** + * @param PluginBase $owner - plugin that owns this command + */ public function __construct($owner,$fmt = "/%s %s %s") { parent::__construct($owner); $this->enableSCmd("help",["aliases"=>["?"]]); $this->fmt = $fmt; } + /** + * Entry point for sub-commands. Will show the help or usage messages + * + * @param CommandSender $c - Entity issuing the command + * @param Command $cc - actual command that was issued + * @param str $scmd - sub-command being executed + * @param mixed $data - Additional data passed to sub-command (global options) + * @param str[] $args - arguments for sub-command + */ public function onSCommand(CommandSender $c,Command $cc,$scmd,$data,array $args) { $cm = $this->owner->getSCmdMap(); $pageNumber = $this->getPageNumber($args); if (count($args)) { if ($args[0] == "usage") { - if (!isset($cm["usage"][$scmd])) return false; + if ($cm->getUsage($scmd) === null) return false; $c->sendMessage(TextFormat::RED.mc::_("Usage: "). sprintf($this->fmt, $cc->getName(), - $scmd, - $cm["usage"][$scmd])); + $scmd, $cm->getUsage($scmd))); return true; } $txt = [ "Help for ".$cc->getName() ]; foreach ($args as $i) { - if (isset($cm["alias"][$i])) $i=$cm["alias"][$i]; - if (!isset($cm["help"][$i]) && !isset($cm["usage"][$i])) { + if ($cm->getAlias($i) !== null) $i=$cm->getAlias($i); + if ($cm->getHelpMsg($i) === null && $cm->getUsage($i) === null) { $txt[] = TextFormat::RED.mc::_("No help for %1%",$i); continue; } + $txt[] = TextFormat::YELLOW.mc::_("Help: ").TextFormat::WHITE. "/".$cc->getName()." $i"; - if (isset($cm["help"][$i])) + if ($cm->getHelpMsg($i) !== null) $txt[] = TextFormat::YELLOW.mc::_("Description: "). - TextFormat::WHITE.$cm["help"][$i]; - if (isset($cm["usage"][$i])) + TextFormat::WHITE.$cm->getHelpMsg($i); + if ($cm->getUsage($i) !== null) $txt[] = TextFormat::YELLOW.mc::_("Usage: "). TextFormat::WHITE. - sprintf($this->fmt,$cc->getName(),$i,$cm["usage"][$i]); + sprintf($this->fmt,$cc->getName(),$i,$cm->getUsage($i)); } return $this->paginateText($c,$pageNumber,$txt); } - ksort($cm["help"]); + $txt = [ mc::_("Available sub-commands for %1%",$cc->getName()) ]; - foreach ($cm["help"] as $cn => $desc) { + foreach ($cm->getHelp() as $cn => $desc) { $ln = TextFormat::GREEN.$cn; - foreach ($cm["alias"] as $i => $j) { + foreach ($cm->getAliases() as $i => $j) { if ($j == $cn) $ln .= "|$i"; } $ln .= ": ".TextFormat::WHITE.$desc; diff --git a/src/aliuly/manyworlds/common/BasicPlugin.php b/src/aliuly/manyworlds/common/BasicPlugin.php index 6c03c80..2405759 100644 --- a/src/aliuly/manyworlds/common/BasicPlugin.php +++ b/src/aliuly/manyworlds/common/BasicPlugin.php @@ -1,23 +1,37 @@ $j) { @@ -33,6 +47,21 @@ protected function modConfig($ns,$mods,$defaults,$xhlp="") { } if (!$j) continue; $class = $mods[$i][0]; + if (is_array($class)) { + while (count($class) > 1) { + // All classes before the last one are dependencies... + $classname = $dep = array_shift($class); + if(strpos($classname,"\\") === false) $classname = $ns."\\".$classname; + if (isset($this->modules[$dep])) continue; // Dependancy already loaded + if(isset($cfg[strtolower($dep)])) { + $this->modules[$dep] = new $classname($this,$cfg[strtolower($dep)]); + } else { + $this->modules[$dep] = new $classname($this); + } + } + // The last class in the array implements the actual feature + $class = array_shift($class); + } if(strpos($class,"\\") === false) $class = $ns."\\".$class; if (isset($cfg[$i])) $this->modules[$i] = new $class($this,$cfg[$i]); @@ -44,17 +73,37 @@ protected function modConfig($ns,$mods,$defaults,$xhlp="") { $this->getLogger()->info(mc::_("NO features enabled")); return; } - $this->state = []; - $this->getServer()->getPluginManager()->registerEvents($this, $this); + $this->session = null; $this->getLogger()->info(mc::n(mc::_("Enabled one feature"), - mc::_("Enable %1% features",$c), + mc::_("Enabled %1% features",$c), $c)); - if (count($this->scmdMap) && count($this->scmdMap["mgrs"])) { + if ($this->scmdMap !== null && $this->scmdMap->getCommandCount() > 0) { $this->modules[] = new BasicHelp($this,$xhlp); } return $cfg; } - + /** + * Get module + * @param str $module - module to retrieve + * @return mixed|null + */ + public function getModule($str) { + if (isset($this->modules[$str])) return $this->modules[$str]; + return null; + } + /** + * Get Modules array + * @return array + */ + public function getModules() { + return $this->modules; + } + /** + * Save a config section to the plugins' config.yml + * + * @param str $key - section to save + * @param mixed $settings - settings to save + */ public function cfgSave($key,$settings) { $cfg=new Config($this->getDataFolder()."config.yml",Config::YAML); $dat = $cfg->getAll(); @@ -62,90 +111,82 @@ public function cfgSave($key,$settings) { $cfg->setAll($dat); $cfg->save(); } - - protected function initSCmdMap() { - $this->scmdMap = [ - "mgrs" => [], - "help" => [], - "usage" => [], - "alias" => [], - "permission" => [], - ]; - } + /** + * Dispatch commands using sub command table + */ protected function dispatchSCmd(CommandSender $sender,Command $cmd,array $args,$data=null) { - if (count($args) == 0) { - $sender->sendMessage(mc::_("No sub-command specified")); - return false; - } - $scmd = strtolower(array_shift($args)); - if (isset($this->scmdMap["alias"][$scmd])) { - $scmd = $this->scmdMap["alias"][$scmd]; - } - if (!isset($this->scmdMap["mgrs"][$scmd])) { - $sender->sendMessage(mc::_("Unknown sub-command %2% (try /%1% help)",$cmd->getName(),$scmd)); + if ($this->scmdMap === null) { + $sender->sendMessage(mc::_("No sub-commands available")); return false; } - if (isset($this->scmdMapd["permission"][$scmd])) { - if (!$sender->hasPermission($this->scmdMapd["permission"][$scmd])) { - $sender->sendMessage(mc::_("You are not allowed to do this")); - return true; - } - } - $callback = $this->scmdMap["mgrs"][$scmd]; - if ($callback($sender,$cmd,$scmd,$data,$args)) return true; - if (isset($this->scmdMap["mgrs"]["help"])) { - $callback = $this->scmdMap["mgrs"]["help"]; - return $callback($sender,$cmd,$scmd,$data,["usage"]); - } - return false; + return $this->scmdMap->dispatchSCmd($sender,$cmd,$args,$data); } - + /** Look-up sub command map + * @returns SubCommandMap + */ public function getSCmdMap() { return $this->scmdMap; } + /** + * Register a sub command + * @param str $cmd - sub command + * @param callable $callable - callable to execute + * @param array $opts - additional options + */ public function registerSCmd($cmd,$callable,$opts) { - $cmd = strtolower($cmd); - $this->scmdMap["mgrs"][$cmd] = $callable; - - foreach (["help","usage","permission"] as $p) { - if(isset($opts[$p])) { - $this->scmdMap[$p][$cmd] = $opts[$p]; - } + if ($this->scmdMap === null) { + $this->scmdMap = new SubCommandMap(); } - if (isset($opts["aliases"])) { - foreach ($opts["aliases"] as $alias) { - $this->scmdMap["alias"][$alias] = $cmd; - } - } - } - - public function onPlayerQuit(PlayerQuitEvent $ev) { - $n = strtolower($ev->getPlayer()->getName()); - if (isset($this->state[$n])) unset($this->state[$n]); + $this->scmdMap->registerSCmd($cmd,$callable,$opts); } - + /** + * Get a player state for the desired module/$label. + * + * @param str $label - state variable to get + * @param Player|str $player - Player instance or name + * @param mixed $default - default value to return is no state found + * @return mixed + */ public function getState($label,$player,$default) { - if ($player instanceof CommandSender) $player = $player->getName(); - $player = strtolower($player); - if (!isset($this->state[$player])) return $default; - if (!isset($this->state[$player][$label])) return $default; - return $this->state[$player][$label]; + if ($this->session === null) return $default; + return $this->session->getState($label,$player,$default); } - + /** + * Set a player related state + * + * @param str $label - state variable to set + * @param Player|str $player - player instance or their name + * @param mixed $val - value to set + * @return mixed + */ public function setState($label,$player,$val) { - if ($player instanceof CommandSender) $player = $player->getName(); - $player = strtolower($player); - if (!isset($this->state[$player])) $this->state[$player] = []; - $this->state[$player][$label] = $val; + if ($this->session === null) $this->session = new Session($this); + return $this->session->setState($label,$player,$val); } - + /** + * Clears a player related state + * + * @param str $label - state variable to clear + * @param Player|str $player - intance of Player or their name + */ public function unsetState($label,$player) { - if ($player instanceof CommandSender) $player = $player->getName(); - $player = strtolower($player); - if (!isset($this->state[$player])) return; - if (!isset($this->state[$player][$label])) return; - unset($this->state[$player][$label]); + if ($this->session === null) return; + $this->session->unsetState($label,$player); } - + /** + * Gets the contents of an embedded resource on the plugin file. + * + * @param string $filename + * @return string|null + */ + public function getResourceContents($filename){ + $fp = $this->getResource($filename); + if($fp === null){ + return null; + } + $contents = stream_get_contents($fp); + fclose($fp); + return $contents; + } } diff --git a/src/aliuly/manyworlds/common/MPMU.php b/src/aliuly/manyworlds/common/MPMU.php index db158a3..06e63b8 100644 --- a/src/aliuly/manyworlds/common/MPMU.php +++ b/src/aliuly/manyworlds/common/MPMU.php @@ -1,24 +1,59 @@ =, <=, <> or !=, =, !|~, <, > + * + * @param str api Installed API version + * @param str version API version to compare against + * + * @return bool + */ static public function apiCheck($api,$version) { switch (substr($version,0,2)) { case ">=": @@ -43,21 +78,12 @@ static public function apiCheck($api,$version) { if (intval($api) != intval($version)) return 0; return version_compare($api,$version) >= 0; } - static public function itemName(Item $item) { - $n = $item->getName(); - if ($n != "Unknown") return $n; - if (count(self::$items) == 0) { - $constants = array_keys((new \ReflectionClass("pocketmine\\item\\Item"))->getConstants()); - foreach ($constants as $constant) { - $id = constant("pocketmine\\item\\Item::$constant"); - $constant = str_replace("_", " ", $constant); - self::$items[$id] = $constant; - } - } - if (isset(self::$items[$item->getId()])) - return self::$items[$item->getId()]; - return $n; - } + /** + * Returns a localized string for the gamemode + * + * @param int mode + * @return str + */ static public function gamemodeStr($mode) { if (class_exists(__NAMESPACE__."\\mc",false)) { switch ($mode) { @@ -76,13 +102,27 @@ static public function gamemodeStr($mode) { } return "$mode-mode"; } - + /** + * Check's player or sender's permissions and shows a message if appropriate + * + * @param CommandSender $sender + * @param str $permission + * @param bool $msg If false, no message is shown + * @return bool + */ static public function access(CommandSender $sender, $permission,$msg=true) { if($sender->hasPermission($permission)) return true; if ($msg) $sender->sendMessage(mc::_("You do not have permission to do that.")); return false; } + /** + * Check's if $sender is a player in game + * + * @param CommandSender $sender + * @param bool $msg If false, no message is shown + * @return bool + */ static public function inGame(CommandSender $sender,$msg = true) { if (!($sender instanceof Player)) { if ($msg) $sender->sendMessage(mc::_("You can only do this in-game")); @@ -90,10 +130,159 @@ static public function inGame(CommandSender $sender,$msg = true) { } return true; } + /** + * Takes a player and creates a string suitable for indexing + * + * @param Player|str $player - Player to index + * @return str + */ static public function iName($player) { if ($player instanceof Player) { $player = strtolower($player->getName()); } return $player; } + /** + * Lile file_get_contents but for a Plugin resource + * + * @param Plugin $plugin + * @param str $filename + * @return str|null + */ + static public function getResourceContents($plugin,$filename) { + $fp = $plugin->getResource($filename); + if($fp === null){ + return null; + } + $contents = stream_get_contents($fp); + fclose($fp); + return $contents; + } + /** + * Call a plugin's function. + * + * If the $plug parameter is given a string, it will simply look for that + * plugin. If an array is provided, it is assumed to be of the form: + * + * [ "plugin", "version" ] + * + * So then it will check that the plugin exists, and the version number + * matches according to the rules from **apiCheck**. + * + * Also, if plugin contains an **api** property, it will use that as + * the class for method calling instead. + * + * @param Server $server - pocketmine server instance + * @param str|array $plug - plugin to call + * @param str $method - method to call + * @param mixed $default - If the plugin does not exist or it is not enable, this value is returned + * @return mixed + */ + static public function callPlugin($server,$plug,$method,$args,$default = null) { + $v = null; + if (is_array($plug)) list($plug,$v) = $plug; + if (($plugin = $server->getPluginManager()->getPlugin($plug)) === null + || $plugin->isEnabled()) return $default; + + if ($v !== null && !self::apiCheck($plugin->getDescription()->getVersion(),$v)) return $default; + if (property_exists($plugin,"api")) { + $fn = [ $plugin->api , $method ]; + } else { + $fn = [ $plugin, $method ]; + } + if (!is_callable($fn)) return $default; + return $fn(...$args); + } + /** + * Register a command + * + * @param Plugin $plugin - plugin that "owns" the command + * @param CommandExecutor $executor - object that will be called onCommand + * @param str $cmd - Command name + * @param array $yaml - Additional settings for this command. + * @deprecated Moved to Cmd class + */ + static public function addCommand($plugin, $executor, $cmd, $yaml) { + $newCmd = new \pocketmine\command\PluginCommand($cmd,$plugin); + if (isset($yaml["description"])) + $newCmd->setDescription($yaml["description"]); + if (isset($yaml["usage"])) + $newCmd->setUsage($yaml["usage"]); + if(isset($yaml["aliases"]) and is_array($yaml["aliases"])) { + $aliasList = []; + foreach($yaml["aliases"] as $alias) { + if(strpos($alias,":")!== false) { + $this->owner->getLogger()->info("Unable to load alias $alias"); + continue; + } + $aliasList[] = $alias; + } + $newCmd->setAliases($aliasList); + } + if(isset($yaml["permission"])) + $newCmd->setPermission($yaml["permission"]); + if(isset($yaml["permission-message"])) + $newCmd->setPermissionMessage($yaml["permission-message"]); + $newCmd->setExecutor($executor); + $cmdMap = $plugin->getServer()->getCommandMap(); + $cmdMap->register($plugin->getDescription()->getName(),$newCmd); + } + /** + * Unregisters a command + * @param Server|Plugin $obj - Access path to server instance + * @param str $cmd - Command name to remove + * @deprecated Moved to Cmd class + */ + static public function rmCommand($srv, $cmd) { + $cmdMap = $srv->getCommandMap(); + $oldCmd = $cmdMap->getCommand($cmd); + if ($oldCmd === null) return false; + $oldCmd->setLabel($cmd."_disabled"); + $oldCmd->unregister($cmdMap); + return true; + } + /** + * Send a PopUp, but takes care of checking if there are some + * plugins that might cause issues. + * + * Currently only supports SimpleAuth and BasicHUD. + * + * @param Player $player + * @param str $msg + */ + static public function sendPopup($player,$msg) { + $pm = $player->getServer()->getPluginManager(); + if (($sa = $pm->getPlugin("SimpleAuth")) !== null) { + // SimpleAuth also has a HUD when not logged in... + if ($sa->isEnabled() && !$sa->isPlayerAuthenticated($player)) return; + } + if (($hud = $pm->getPlugin("BasicHUD")) !== null) { + // Send pop-ups through BasicHUD + $hud->sendPopup($player,$msg); + return; + } + $player->sendPopup($msg); + } + /** + * Check prefixes + * @param str $txt - input text + * @param str $tok - keyword to test + * @return str|null + */ + static public function startsWith($txt,$tok) { + $ln = strlen($tok); + if (strtolower(substr($txt,0,$ln)) != $tok) return null; + return trim(substr($txt,$ln)); + } + /** + * Look-up player + * @param CommandSender $req + * @param str $n + */ + static public function getPlayer(CommandSender $c,$n) { + $pl = $c->getServer()->getPlayer($n); + if ($pl === null) $c->sendMessage(mc::_("%1% not found", $n)); + return $pl; + } + } diff --git a/src/aliuly/manyworlds/common/Session.php b/src/aliuly/manyworlds/common/Session.php new file mode 100644 index 0000000..81dfefc --- /dev/null +++ b/src/aliuly/manyworlds/common/Session.php @@ -0,0 +1,79 @@ +plugin = $owner; + $this->plugin->getServer()->getPluginManager()->registerEvents($this,$this->plugin); + $this->state = []; + } + /** + * Handle player quit events. Free's data used by the state tracking + * code. + * + * @param PlayerQuitEvent $ev - Quit event + */ + public function onPlayerQuit(PlayerQuitEvent $ev) { + $n = strtolower($ev->getPlayer()->getName()); + if (isset($this->state[$n])) unset($this->state[$n]); + } + /** + * Get a player state for the desired module/$label. + * + * @param str $label - state variable to get + * @param Player|str $player - Player instance or name + * @param mixed $default - default value to return is no state found + * @return mixed + */ + public function getState($label,$player,$default) { + if ($player instanceof CommandSender) $player = $player->getName(); + $player = strtolower($player); + if (!isset($this->state[$player])) return $default; + if (!isset($this->state[$player][$label])) return $default; + return $this->state[$player][$label]; + } + /** + * Set a player related state + * + * @param str $label - state variable to set + * @param Player|str $player - player instance or their name + * @param mixed $val - value to set + * @return mixed + */ + public function setState($label,$player,$val) { + if ($player instanceof CommandSender) $player = $player->getName(); + $player = strtolower($player); + if (!isset($this->state[$player])) $this->state[$player] = []; + $this->state[$player][$label] = $val; + return $val; + } + /** + * Clears a player related state + * + * @param str $label - state variable to clear + * @param Player|str $player - intance of Player or their name + */ + public function unsetState($label,$player) { + if ($player instanceof CommandSender) $player = $player->getName(); + $player = strtolower($player); + if (!isset($this->state[$player])) return; + if (!isset($this->state[$player][$label])) return; + unset($this->state[$player][$label]); + } + +} diff --git a/src/aliuly/manyworlds/common/SubCommandMap.php b/src/aliuly/manyworlds/common/SubCommandMap.php new file mode 100644 index 0000000..9254e31 --- /dev/null +++ b/src/aliuly/manyworlds/common/SubCommandMap.php @@ -0,0 +1,93 @@ +executors = []; + $this->help = []; + $this->usage = []; + $this->aliases = []; + $this->permission = []; + } + /** + * Returns the number of commands configured + */ + public function getCommandCount() { + return count($this->executors); + } + /** + * Dispatch commands using sub command table + */ + public function dispatchSCmd(CommandSender $sender,Command $cmd,array $args,$data=null) { + if (count($args) == 0) { + $sender->sendMessage(mc::_("No sub-command specified")); + return false; + } + $scmd = strtolower(array_shift($args)); + if (isset($this->aliases[$scmd])) { + $scmd = $this->aliases[$scmd]; + } + if (!isset($this->executors[$scmd])) { + $sender->sendMessage(mc::_("Unknown sub-command %2% (try /%1% help)",$cmd->getName(),$scmd)); + return false; + } + if (isset($this->permission[$scmd])) { + if (!$sender->hasPermission($this->permission[$scmd])) { + $sender->sendMessage(mc::_("You are not allowed to do this")); + return true; + } + } + $callback = $this->executors[$scmd]; + if ($callback($sender,$cmd,$scmd,$data,$args)) return true; + if (isset($this->executors["help"])) { + $callback = $this->executors["help"]; + return $callback($sender,$cmd,$scmd,$data,["usage"]); + } + return false; + } + /** + * Register a sub command + * @param str $cmd - sub command + * @param callable $callable - callable to execute + * @param array $opts - additional options + */ + public function registerSCmd($cmd,$callable,$opts) { + $cmd = strtolower($cmd); + $this->executors[$cmd] = $callable; + + if (isset($opts["help"])) { + $this->help[$cmd] = $opts["help"]; + ksort($this->help); + } + if (isset($opts["usage"])) $this->usage[$cmd] = $opts["usage"]; + if (isset($opts["permission"])) $this->permission[$cmd] = $opts["permission"]; + if (isset($opts["aliases"])) { + foreach ($opts["aliases"] as $alias) { + $this->aliases[$alias] = $cmd; + } + } + } + + public function getUsage($scmd) { + return isset($this->usage[$scmd]) ? $this->usage[$scmd] : null; + } + public function getAlias($scmd) { + return isset($this->aliases[$scmd]) ? $this->aliases[$scmd] : null; + } + public function getHelpMsg($scmd) { + return isset($this->help[$scmd]) ? $this->help[$scmd] : null; + } + public function getHelp() { + return $this->help; + } + public function getAliases() { + return $this->aliases; + } +} diff --git a/src/aliuly/manyworlds/common/mc.php b/src/aliuly/manyworlds/common/mc.php index 9049bf3..9d6f597 100644 --- a/src/aliuly/manyworlds/common/mc.php +++ b/src/aliuly/manyworlds/common/mc.php @@ -1,18 +1,41 @@ getFile()); + * * mc::_("string to translate\n") + * * mc::_("string to translate %1% %2%\n",$arg1,$arg2) + * * mc::n(mc::\_("singular form"),mc::\_("Plural form"),$count) + */ abstract class mc { - /* - * Use xgettext --no-wrap [-j] [-o file] to extract messages - * -j is to join existing message files - * -o output file + /** @var str[] $txt Message translations */ + public static $txt = []; + /** Main translation function + * + * This translates strings. The naming of "_" is to make it compatible + * with gettext utilities. The string can contain "%1%", "%2%, etc... + * These are inserted from the following arguments. Use "%%" to insert + * a single "%". * - * Usage: - * mc::load("messages.po|messages.ini"); - * mc::_("string to translate\n") - * mc::n(mc::_("Plural for one"),mc::_("Plural for not one"),$count) + * @param str[] $args - messages + * @return str translated string */ - public static $txt = []; public static function _(...$args) { $fmt = array_shift($args); if (isset(self::$txt[$fmt])) $fmt = self::$txt[$fmt]; @@ -27,21 +50,41 @@ public static function _(...$args) { } return $fmt; } + /** + * Plural and singular forms. + * + * @param str $a - Singular form + * @param str $b - Plural form + * @param int $c - the number to test to select between $a or $b + * @return str - Either plural or singular forms depending on the value of $c + */ public static function n($a,$b,$c) { return $c == 1 ? $a : $b; } + /** + * Load a message file for a PocketMine plugin. Only uses .ini files. + * + * @param Plugin $plugin - owning plugin + * @param str $path - output of $plugin->getFile() + * @return int|false - false on error or the number of messages loaded + */ public static function plugin_init($plugin,$path) { if (file_exists($plugin->getDataFolder()."messages.ini")) { - self::load($plugin->getDataFolder()."messages.ini"); - return; + return self::load($plugin->getDataFolder()."messages.ini"); } $msgs = $path."resources/messages/". $plugin->getServer()->getProperty("settings.language"). ".ini"; - if (!file_exists($msgs)) return; - mc::load($msgs); + if (!file_exists($msgs)) return false; + return self::load($msgs); } + /** + * Load the specified message catalogue. + * Can read .ini or .po files. + * @param str $f - Filename to load + * @return int|false - returns the number of strings loaded or false on error + */ public static function load($f) { $potxt = "\n".file_get_contents($f)."\n"; if (preg_match('/\nmsgid\s/',$potxt)) { @@ -57,7 +100,7 @@ public static function load($f) { if ($mm[2][$i] == "") continue; eval('$a = "'.$mm[1][$i].'";'); eval('$b = "'.$mm[2][$i].'";'); - mc::$txt[$a] = $b; + self::$txt[$a] = $b; } return $c; }