Firebombing of commands for the first time in 10 years

This commit is contained in:
Dylan K. Taylor
2025-10-12 03:30:30 +01:00
parent 01092fd8a9
commit 50976c20aa
66 changed files with 2279 additions and 1486 deletions

8
composer.lock generated
View File

@@ -349,12 +349,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/CallbackValidator.git", "url": "https://github.com/pmmp/CallbackValidator.git",
"reference": "8e0e3be58e89c2611beac9c6954a036b81b2b6fc" "reference": "431c7aa5685d4d756bc74a78e5cd130a54d6fdf8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/8e0e3be58e89c2611beac9c6954a036b81b2b6fc", "url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/431c7aa5685d4d756bc74a78e5cd130a54d6fdf8",
"reference": "8e0e3be58e89c2611beac9c6954a036b81b2b6fc", "reference": "431c7aa5685d4d756bc74a78e5cd130a54d6fdf8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -391,7 +391,7 @@
"issues": "https://github.com/pmmp/CallbackValidator/issues", "issues": "https://github.com/pmmp/CallbackValidator/issues",
"source": "https://github.com/pmmp/CallbackValidator/tree/rewrite" "source": "https://github.com/pmmp/CallbackValidator/tree/rewrite"
}, },
"time": "2025-10-10T21:55:21+00:00" "time": "2025-10-11T15:05:09+00:00"
}, },
{ {
"name": "pocketmine/color", "name": "pocketmine/color",

View File

@@ -29,13 +29,14 @@ use pocketmine\utils\Utils;
/** /**
* @phpstan-type Execute \Closure(CommandSender $sender, Command $command, string $commandLabel, list<string> $args) : mixed * @phpstan-type Execute \Closure(CommandSender $sender, Command $command, string $commandLabel, list<string> $args) : mixed
*/ */
final class ClosureCommand extends Command{ final class ClosureCommand extends LegacyCommand{
/** @phpstan-var Execute */ /** @phpstan-var Execute */
private \Closure $execute; private \Closure $execute;
/** /**
* @param string[] $permissions * @param string[] $permissions
* @phpstan-param Execute $execute * @phpstan-param Execute $execute
* @phpstan-param list<string> $permissions
*/ */
public function __construct( public function __construct(
string $namespace, string $namespace,

View File

@@ -26,34 +26,40 @@ declare(strict_types=1);
*/ */
namespace pocketmine\command; namespace pocketmine\command;
use pocketmine\command\utils\CommandException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable; use pocketmine\lang\Translatable;
use pocketmine\permission\PermissionManager; use pocketmine\player\Player;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\BroadcastLoggerForwarder;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function explode; use function array_unique;
use function count;
use function implode; use function implode;
use function str_replace; use function str_replace;
use function strtolower; use function strtolower;
use function trim; use function trim;
use const PHP_INT_MAX;
abstract class Command{ abstract class Command{
private readonly string $namespace; private readonly string $namespace;
private readonly string $name; private readonly string $name;
/** @var string[] */
private array $permission = [];
private Translatable|string|null $permissionMessage = null; private Translatable|string|null $permissionMessage = null;
/**
* @param CommandOverload[] $overloads
* @phpstan-param list<CommandOverload> $overloads
*/
public function __construct( public function __construct(
string $namespace, string $namespace,
string $name, string $name,
private array $overloads,
private Translatable|string $description = "", private Translatable|string $description = "",
private Translatable|string|null $usageMessage = null
){ ){
if(count($this->overloads) === 0){
throw new \InvalidArgumentException("At least one overload must be provided (extend LegacyCommand for classic execute())");
}
if($namespace === ""){ if($namespace === ""){
throw new \InvalidArgumentException("Command namespace cannot be empty (set it to, for example, your plugin's name)"); throw new \InvalidArgumentException("Command namespace cannot be empty (set it to, for example, your plugin's name)");
} }
@@ -65,14 +71,57 @@ abstract class Command{
$this->name = trim($name); $this->name = trim($name);
} }
final public function executeOverloaded(CommandSender $sender, string $aliasUsed, string $rawArgs) : bool{
foreach($this->overloads as $k => $overload){
if(!$overload->senderHasAnyPermissions($sender)){
continue;
}
try{
$overload->invoke($sender, $aliasUsed, $rawArgs);
return true;
}catch(InvalidCommandSyntaxException $e){
\GlobalLogger::get()->debug("Overload $k of /$aliasUsed rejected: " . $e->getMessage());
}
}
$usages = $this->getUsages($sender, $aliasUsed);
if(count($usages) === 0){
$message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($aliasUsed);
if($message instanceof Translatable){
$sender->sendMessage($message->prefix(TextFormat::RED));
}elseif($message !== ""){
$permissions = [];
foreach($this->overloads as $overload){
foreach($overload->getPermissions() as $permission){
$permissions[] = $permission;
}
}
$permissions = array_unique($permissions);
$sender->sendMessage(str_replace("<permission>", implode(";", $permissions), $message));
}
return false;
}
foreach($usages as $usageMessage){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($usageMessage)));
}
return false;
}
/** /**
* @param string[] $args * @return Translatable[]
* @phpstan-param list<string> $args * @phpstan-return list<Translatable>
*
* @return mixed
* @throws CommandException
*/ */
abstract public function execute(CommandSender $sender, string $commandLabel, array $args); public function getUsages(CommandSender $sender, string $aliasUsed) : array{
$usages = [];
foreach($this->overloads as $overload){
if($overload->senderHasAnyPermissions($sender)){
$usages[] = new Translatable("/$aliasUsed {%0}", [$overload->getUsage()]);
}
}
return $usages;
}
final public function getNamespace() : string{ final public function getNamespace() : string{
return $this->namespace; return $this->namespace;
@@ -93,59 +142,17 @@ abstract class Command{
return "$this->namespace:$this->name"; return "$this->namespace:$this->name";
} }
/**
* @return string[]
*/
public function getPermissions() : array{
return $this->permission;
}
/** /**
* @param string[] $permissions * @param string[] $permissions
* @phpstan-param list<string> $permissions
*/ */
public function setPermissions(array $permissions) : void{ protected function sendBadPermissionMessage(string $context, CommandSender $sender, array $permissions) : void{
$permissionManager = PermissionManager::getInstance();
foreach($permissions as $perm){
if($permissionManager->getPermission($perm) === null){
throw new \InvalidArgumentException("Cannot use non-existing permission \"$perm\"");
}
}
$this->permission = $permissions;
}
public function setPermission(?string $permission) : void{
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
}
/**
* @param string $context usually the command name, but may include extra args if useful (e.g. for subcommands)
* @param CommandSender $target the target to check the permission for
* @param string|null $permission the permission to check, if null, will check if the target has any of the command's permissions
*/
public function testPermission(string $context, CommandSender $target, ?string $permission = null) : bool{
if($this->testPermissionSilent($target, $permission)){
return true;
}
$message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($context); $message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($context);
if($message instanceof Translatable){ if($message instanceof Translatable){
$target->sendMessage($message->prefix(TextFormat::RED)); $sender->sendMessage($message->prefix(TextFormat::RED));
}elseif($message !== ""){ }elseif($message !== ""){
$target->sendMessage(str_replace("<permission>", $permission ?? implode(";", $this->permission), $message)); $sender->sendMessage(str_replace("<permission>", implode(";", $permissions), $message));
} }
return false;
}
public function testPermissionSilent(CommandSender $target, ?string $permission = null) : bool{
$list = $permission !== null ? [$permission] : $this->permission;
foreach($list as $p){
if($target->hasPermission($p)){
return true;
}
}
return false;
} }
public function getPermissionMessage() : Translatable|string|null{ public function getPermissionMessage() : Translatable|string|null{
@@ -156,10 +163,6 @@ abstract class Command{
return $this->description; return $this->description;
} }
public function getUsage() : Translatable|string|null{
return $this->usageMessage;
}
public function setDescription(Translatable|string $description) : void{ public function setDescription(Translatable|string $description) : void{
$this->description = $description; $this->description = $description;
} }
@@ -168,10 +171,6 @@ abstract class Command{
$this->permissionMessage = $permissionMessage; $this->permissionMessage = $permissionMessage;
} }
public function setUsage(Translatable|string|null $usage) : void{
$this->usageMessage = $usage;
}
public static function broadcastCommandMessage(CommandSender $source, Translatable|string $message, bool $sendToSource = true) : void{ public static function broadcastCommandMessage(CommandSender $source, Translatable|string $message, bool $sendToSource = true) : void{
$users = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); $users = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
$result = KnownTranslationFactory::chat_type_admin($source->getName(), $message); $result = KnownTranslationFactory::chat_type_admin($source->getName(), $message);
@@ -189,4 +188,31 @@ abstract class Command{
} }
} }
} }
protected static function fetchPermittedPlayerTarget(
CommandSender $sender,
?string $target,
string $selfPermission,
string $otherPermission
) : ?Player{
if($target !== null){
$player = $sender->getServer()->getPlayerByPrefix($target);
}elseif($sender instanceof Player){
$player = $sender;
}else{
throw new InvalidCommandSyntaxException();
}
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return null;
}
$permission = $player === $sender ? $selfPermission : $otherPermission;
if(!$sender->hasPermission($permission)){
$sender->sendMessage(TextFormat::RED . "You don't have permission to use this command on others");
return null;
}
return $player;
}
} }

View File

@@ -24,20 +24,18 @@ declare(strict_types=1);
namespace pocketmine\command; namespace pocketmine\command;
use pocketmine\command\utils\CommandStringHelper; use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function array_shift; use function addcslashes;
use function count; use function count;
use function implode; use function implode;
use function preg_match; use function preg_match;
use function str_contains;
use function strlen; use function strlen;
use function strpos; use function strpos;
use function substr; use function substr;
class FormattedCommandAlias extends Command{ class FormattedCommandAlias extends LegacyCommand{
/** /**
* - matches a $ * - matches a $
* - captures an optional second $ to indicate required/optional * - captures an optional second $ to indicate required/optional
@@ -77,7 +75,7 @@ class FormattedCommandAlias extends Command{
$processedArgs[] = $processedArg; $processedArgs[] = $processedArg;
} }
} }
$commands[] = $processedArgs; $commands[] = implode(" ", $processedArgs);
}catch(\InvalidArgumentException $e){ }catch(\InvalidArgumentException $e){
$sender->sendMessage(TextFormat::RED . $e->getMessage()); $sender->sendMessage(TextFormat::RED . $e->getMessage());
return false; return false;
@@ -85,38 +83,13 @@ class FormattedCommandAlias extends Command{
} }
$commandMap = $sender->getServer()->getCommandMap(); $commandMap = $sender->getServer()->getCommandMap();
foreach($commands as $commandArgs){ foreach($commands as $commandLine){
//this approximately duplicates the logic found in SimpleCommandMap::dispatch() $sender->getServer()->getLogger()->debug("Dispatching formatted command: $commandLine");
//this is to allow directly invoking the commands without having to rebuild a command string and parse it if(!$commandMap->dispatch($sender, $commandLine)){
//again for no reason
//TODO: a method on CommandMap to invoke a command with pre-parsed arguments would probably be a good idea
//for a future major version
$commandLabel = array_shift($commandArgs);
if($commandLabel === null){
throw new AssumptionFailedError("This should have been checked before construction");
}
//formatted command aliases don't use user-specific aliases since they are globally defined in pocketmine.yml
//using user-specific aliases might break the behaviour
if(($target = $commandMap->getCommand($commandLabel)) instanceof Command){
$timings = Timings::getCommandDispatchTimings($target->getId());
$timings->startTiming();
try{
$target->execute($sender, $commandLabel, $commandArgs);
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage() ?? "/$commandLabel")));
}finally{
$timings->stopTiming();
}
}else{
//TODO: this seems suspicious - why do we continue alias execution if one of the commands is borked?
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_notFound($commandLabel, "/help")->prefix(TextFormat::RED)));
//to match the behaviour of SimpleCommandMap::dispatch() //to match the behaviour of SimpleCommandMap::dispatch()
//this shouldn't normally happen, but might happen if the command was unregistered or modified after //this shouldn't normally happen, but might happen if the command was unregistered or modified after
//the alias was installed //the alias was installed
//TODO: maybe we should abort command processing if there was an error???
$result = false; $result = false;
} }
} }
@@ -160,6 +133,11 @@ class FormattedCommandAlias extends Command{
$index = $start + strlen($replacement); $index = $start + strlen($replacement);
} }
//we need to assemble a command string to call the target commands, so this needs to be properly quoted
if(str_contains($formatString, " ")){
return '"' . addcslashes($formatString, '"') . '"';
}
return $formatString; return $formatString;
} }

View File

@@ -0,0 +1,140 @@
<?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\command;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\utils\CommandException;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\lang\Translatable;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\permission\PermissionManager;
use function explode;
use const PHP_INT_MAX;
/**
* Offers a quick & dirty upgrade path for old code that can't be quickly migrated to the new overload system in PM6.
* @deprecated
*/
abstract class LegacyCommand extends Command{
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $permission = [];
public function __construct(
string $namespace,
string $name,
Translatable|string $description = "",
private Translatable|string|null $usageMessage = null,
){
parent::__construct($namespace, $name, [new CommandOverload(
[new RawParameter("args", "args")],
DefaultPermissionNames::GROUP_USER,
$this->handler(...),
acceptsAliasUsed: true
)], $description);
}
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getPermissions() : array{
return $this->permission;
}
/**
* @param string[] $permissions
* @phpstan-param list<string> $permissions
*/
public function setPermissions(array $permissions) : void{
$permissionManager = PermissionManager::getInstance();
foreach($permissions as $perm){
if($permissionManager->getPermission($perm) === null){
throw new \InvalidArgumentException("Cannot use non-existing permission \"$perm\"");
}
}
$this->permission = $permissions;
}
public function setPermission(?string $permission) : void{
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
}
/**
* @param string $context usually the command name, but may include extra args if useful (e.g. for subcommands)
* @param CommandSender $target the target to check the permission for
* @param string|null $permission the permission to check, if null, will check if the target has any of the command's permissions
*/
public function testPermission(string $context, CommandSender $target, ?string $permission = null) : bool{
if($this->testPermissionSilent($target, $permission)){
return true;
}
$this->sendBadPermissionMessage($context, $target, $permission !== null ? [$permission] : $this->permission);
return false;
}
public function testPermissionSilent(CommandSender $target, ?string $permission = null) : bool{
$list = $permission !== null ? [$permission] : $this->permission;
foreach($list as $p){
if($target->hasPermission($p)){
return true;
}
}
return false;
}
public function getUsages(CommandSender $sender, string $aliasUsed) : array{
if($this->testPermissionSilent($sender)){
return $this->usageMessage instanceof Translatable ?
[$this->usageMessage] :
[new Translatable("{%0}", [$this->usageMessage ?? "/$aliasUsed"])];
}
return [];
}
private function handler(CommandSender $sender, string $aliasUsed, string $rawArgs = "") : void{
if(!$this->testPermission($aliasUsed, $sender)){
return;
}
$args = CommandStringHelper::parseQuoteAware($rawArgs);
$this->execute($sender, $aliasUsed, $args);
}
/**
* @param string[] $args
* @phpstan-param list<string> $args
*
* @return mixed
* @throws CommandException
*/
abstract protected function execute(CommandSender $sender, string $commandLabel, array $args);
}

View File

@@ -28,7 +28,7 @@ use pocketmine\lang\Translatable;
use pocketmine\plugin\Plugin; use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginOwned; use pocketmine\plugin\PluginOwned;
final class PluginCommand extends Command implements PluginOwned{ final class PluginCommand extends LegacyCommand implements PluginOwned{
public function __construct( public function __construct(
string $namespace, string $namespace,
string $name, string $name,

View File

@@ -66,7 +66,6 @@ use pocketmine\command\defaults\VersionCommand;
use pocketmine\command\defaults\WhitelistCommand; use pocketmine\command\defaults\WhitelistCommand;
use pocketmine\command\defaults\XpCommand; use pocketmine\command\defaults\XpCommand;
use pocketmine\command\utils\CommandStringHelper; use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
@@ -76,9 +75,11 @@ use function array_filter;
use function array_map; use function array_map;
use function array_shift; use function array_shift;
use function count; use function count;
use function explode;
use function implode; use function implode;
use function is_array; use function is_array;
use function is_string; use function is_string;
use function ltrim;
use function str_contains; use function str_contains;
use function strcasecmp; use function strcasecmp;
use function strtolower; use function strtolower;
@@ -146,7 +147,7 @@ class SimpleCommandMap implements CommandMap{
} }
public function register(Command $command, array $otherAliases = []) : void{ public function register(Command $command, array $otherAliases = []) : void{
if(count($command->getPermissions()) === 0){ if($command instanceof LegacyCommand && count($command->getPermissions()) === 0){
throw new \InvalidArgumentException("Commands must have a permission set"); throw new \InvalidArgumentException("Commands must have a permission set");
} }
@@ -172,10 +173,10 @@ class SimpleCommandMap implements CommandMap{
} }
public function dispatch(CommandSender $sender, string $commandLine) : bool{ public function dispatch(CommandSender $sender, string $commandLine) : bool{
$args = CommandStringHelper::parseQuoteAware($commandLine); $parts = explode(" ", ltrim($commandLine), limit: 2);
[$sentCommandLabel, $rawArgs] = count($parts) === 2 ? $parts : [$parts[0], ""];
$sentCommandLabel = array_shift($args); if(($target = $this->getCommand($sentCommandLabel, $sender->getCommandAliasMap())) !== null){
if($sentCommandLabel !== null && ($target = $this->getCommand($sentCommandLabel, $sender->getCommandAliasMap())) !== null){
if(is_array($target)){ if(is_array($target)){
self::handleConflicted($sender, $sentCommandLabel, $target, $this->aliasMap); self::handleConflicted($sender, $sentCommandLabel, $target, $this->aliasMap);
return true; return true;
@@ -184,12 +185,7 @@ class SimpleCommandMap implements CommandMap{
$timings->startTiming(); $timings->startTiming();
try{ try{
if($target->testPermission($sentCommandLabel, $sender)){ $target->executeOverloaded($sender, $sentCommandLabel, $rawArgs);
$target->execute($sender, $sentCommandLabel, $args);
}
}catch(InvalidCommandSyntaxException $e){
//TODO: localised command message should use user-provided alias, it shouldn't be hard-baked into the language strings
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage() ?? "/$sentCommandLabel")));
}finally{ }finally{
$timings->stopTiming(); $timings->stopTiming();
} }
@@ -198,7 +194,7 @@ class SimpleCommandMap implements CommandMap{
//Don't love hardcoding the command ID here, but it seems like the only way for now //Don't love hardcoding the command ID here, but it seems like the only way for now
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound( $sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound(
$sentCommandLabel ?? "", $sentCommandLabel,
"/" . $sender->getCommandAliasMap()->getPreferredAlias("pocketmine:help", $this->aliasMap) "/" . $sender->getCommandAliasMap()->getPreferredAlias("pocketmine:help", $this->aliasMap)
)->prefix(TextFormat::RED)); )->prefix(TextFormat::RED));
return false; return false;
@@ -214,7 +210,7 @@ class SimpleCommandMap implements CommandMap{
$candidates = []; $candidates = [];
$userAliasMap = $sender->getCommandAliasMap(); $userAliasMap = $sender->getCommandAliasMap();
foreach($conflictedEntries as $c){ foreach($conflictedEntries as $c){
if($c->testPermissionSilent($sender)){ if(count($c->getUsages($sender, $alias)) > 0){
$candidates[] = "/" . $c->getId(); $candidates[] = "/" . $c->getId();
} }
} }

View File

@@ -25,42 +25,38 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use function array_shift;
use function count;
use function implode;
class BanCommand extends VanillaCommand{ class BanCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[
new StringParameter("playerName", "player name"),
new RawParameter("reason", "reason")
],
DefaultPermissionNames::COMMAND_BAN_PLAYER,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_ban_player_description(), KnownTranslationFactory::pocketmine_command_ban_player_description(),
KnownTranslationFactory::commands_ban_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_BAN_PLAYER);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $playerName, string $reason) : void{
if(count($args) === 0){ $sender->getServer()->getNameBans()->addBan($playerName, $reason, null, $sender->getName());
throw new InvalidCommandSyntaxException();
}
$name = array_shift($args); if(($player = $sender->getServer()->getPlayerExact($playerName)) instanceof Player){
$reason = implode(" ", $args);
$sender->getServer()->getNameBans()->addBan($name, $reason, null, $sender->getName());
if(($player = $sender->getServer()->getPlayerExact($name)) instanceof Player){
$player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_ban($reason) : KnownTranslationFactory::pocketmine_disconnect_ban_noReason()); $player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_ban($reason) : KnownTranslationFactory::pocketmine_disconnect_ban_noReason());
} }
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_ban_success($player !== null ? $player->getName() : $name)); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_ban_success($player !== null ? $player->getName() : $playerName));
return true;
} }
} }

View File

@@ -25,56 +25,51 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use function array_shift;
use function count;
use function implode;
use function inet_pton; use function inet_pton;
class BanIpCommand extends VanillaCommand{ class BanIpCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[
//TODO: maybe split this into two overloads?
new StringParameter("target", "name or IP address"),
new RawParameter("reason", "reason")
],
DefaultPermissionNames::COMMAND_BAN_IP,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_ban_ip_description(), KnownTranslationFactory::pocketmine_command_ban_ip_description(),
KnownTranslationFactory::commands_banip_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_BAN_IP);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $target, string $reason) : void{
if(count($args) === 0){ if(inet_pton($target) !== false){
throw new InvalidCommandSyntaxException(); self::processIPBan($target, $sender, $reason);
}
$value = array_shift($args); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_banip_success($target));
$reason = implode(" ", $args);
if(inet_pton($value) !== false){
$this->processIPBan($value, $sender, $reason);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_banip_success($value));
}else{ }else{
if(($player = $sender->getServer()->getPlayerByPrefix($value)) instanceof Player){ if(($player = $sender->getServer()->getPlayerByPrefix($target)) instanceof Player){
$ip = $player->getNetworkSession()->getIp(); $ip = $player->getNetworkSession()->getIp();
$this->processIPBan($ip, $sender, $reason); self::processIPBan($ip, $sender, $reason);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_banip_success_players($ip, $player->getName())); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_banip_success_players($ip, $player->getName()));
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_banip_invalid()); $sender->sendMessage(KnownTranslationFactory::commands_banip_invalid());
return false;
} }
} }
return true;
} }
private function processIPBan(string $ip, CommandSender $sender, string $reason) : void{ private static function processIPBan(string $ip, CommandSender $sender, string $reason) : void{
$sender->getServer()->getIPBans()->addBan($ip, $reason, null, $sender->getName()); $sender->getServer()->getIPBans()->addBan($ip, $reason, null, $sender->getName());
foreach($sender->getServer()->getOnlinePlayers() as $player){ foreach($sender->getServer()->getOnlinePlayers() as $player){

View File

@@ -23,59 +23,50 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\BanEntry; use pocketmine\permission\BanEntry;
use pocketmine\permission\BanList;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function array_map; use function array_map;
use function count; use function count;
use function implode; use function implode;
use function sort; use function sort;
use function strtolower;
use const SORT_STRING; use const SORT_STRING;
class BanListCommand extends VanillaCommand{ class BanListCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[
new CommandOverload(["ips"], DefaultPermissionNames::COMMAND_BAN_LIST, self::listIPBans(...)),
new CommandOverload(["players"], DefaultPermissionNames::COMMAND_BAN_LIST, self::listNameBans(...))
],
KnownTranslationFactory::pocketmine_command_banlist_description(), KnownTranslationFactory::pocketmine_command_banlist_description(),
KnownTranslationFactory::commands_banlist_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_BAN_LIST);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function printList(BanList $list) : string{
if(isset($args[0])){
$args[0] = strtolower($args[0]);
if($args[0] === "ips"){
$list = $sender->getServer()->getIPBans();
}elseif($args[0] === "players"){
$list = $sender->getServer()->getNameBans();
}else{
throw new InvalidCommandSyntaxException();
}
}else{
$list = $sender->getServer()->getNameBans();
$args[0] = "players";
}
$list = array_map(function(BanEntry $entry) : string{ $list = array_map(function(BanEntry $entry) : string{
return $entry->getName(); return $entry->getName();
}, $list->getEntries()); }, $list->getEntries());
sort($list, SORT_STRING); sort($list, SORT_STRING);
$message = implode(", ", $list); return implode(", ", $list);
}
if($args[0] === "ips"){ private static function listIPBans(CommandSender $sender) : void{
$sender->sendMessage(KnownTranslationFactory::commands_banlist_ips((string) count($list))); $list = $sender->getServer()->getIPBans();
}else{ $sender->sendMessage(KnownTranslationFactory::commands_banlist_ips((string) count($list->getEntries())));
$sender->sendMessage(KnownTranslationFactory::commands_banlist_players((string) count($list))); $sender->sendMessage(self::printList($list));
} }
$sender->sendMessage($message); private static function listNameBans(CommandSender $sender) : void{
$list = $sender->getServer()->getNameBans();
return true; $sender->sendMessage(KnownTranslationFactory::commands_banlist_players((string) count($list->getEntries())));
$sender->sendMessage(self::printList($list));
} }
} }

View File

@@ -25,7 +25,11 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\command\overload\StringParameter;
use pocketmine\inventory\Inventory; use pocketmine\inventory\Inventory;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\LegacyStringToItemParser; use pocketmine\item\LegacyStringToItemParser;
@@ -37,42 +41,36 @@ use pocketmine\utils\TextFormat;
use function count; use function count;
use function min; use function min;
class ClearCommand extends VanillaCommand{ class ClearCommand extends Command{
private const SELF_PERM = DefaultPermissionNames::COMMAND_CLEAR_SELF;
private const OTHER_PERM = DefaultPermissionNames::COMMAND_CLEAR_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_clear_description(), [new CommandOverload([
KnownTranslationFactory::pocketmine_command_clear_usage() new StringParameter("playerName", "player name"),
new MappedParameter("targetItem", "item name", static function(string $v) : Item{
try{
return StringToItemParser::getInstance()->parse($v) ?? LegacyStringToItemParser::getInstance()->parse($v);
}catch(LegacyStringToItemParserException $e){
throw new ParameterParseException("Invalid item name: $v");
}
}),
new IntRangeParameter("maxCount", "max count", -1, 32767)
], self::OVERLOAD_PERMS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_clear_description()
); );
$this->setPermissions([DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, ?string $playerName = null, ?Item $targetItem = null, int $maxCount = -1) : void{
if(count($args) > 3){ $target = self::fetchPermittedPlayerTarget($sender, $playerName, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
if($target === null){ if($target === null){
return true; return;
}
$targetItem = null;
$maxCount = -1;
if(isset($args[1])){
try{
$targetItem = StringToItemParser::getInstance()->parse($args[1]) ?? LegacyStringToItemParser::getInstance()->parse($args[1]);
if(isset($args[2])){
$targetItem->setCount($maxCount = $this->getInteger($sender, $args[2], -1));
}
}catch(LegacyStringToItemParserException $e){
//vanilla checks this at argument parsing layer, can't come up with a better alternative
$sender->sendMessage(KnownTranslationFactory::commands_give_item_notFound($args[1])->prefix(TextFormat::RED));
return true;
}
} }
/** /**
@@ -87,27 +85,27 @@ class ClearCommand extends VanillaCommand{
// Checking player's inventory for all the items matching the criteria // Checking player's inventory for all the items matching the criteria
if($targetItem !== null && $maxCount === 0){ if($targetItem !== null && $maxCount === 0){
$count = $this->countItems($inventories, $targetItem); $count = self::countItems($inventories, $targetItem);
if($count > 0){ if($count > 0){
$sender->sendMessage(KnownTranslationFactory::commands_clear_testing($target->getName(), (string) $count)); $sender->sendMessage(KnownTranslationFactory::commands_clear_testing($target->getName(), (string) $count));
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_clear_failure_no_items($target->getName())->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_clear_failure_no_items($target->getName())->prefix(TextFormat::RED));
} }
return true; return;
} }
$clearedCount = 0; $clearedCount = 0;
if($targetItem === null){ if($targetItem === null){
// Clear all items from the inventories // Clear all items from the inventories
$clearedCount += $this->countItems($inventories, null); $clearedCount += self::countItems($inventories, null);
foreach($inventories as $inventory){ foreach($inventories as $inventory){
$inventory->clearAll(); $inventory->clearAll();
} }
}else{ }else{
// Clear the item from target's inventory irrelevant of the count // Clear the item from target's inventory irrelevant of the count
if($maxCount === -1){ if($maxCount === -1){
$clearedCount += $this->countItems($inventories, $targetItem); $clearedCount += self::countItems($inventories, $targetItem);
foreach($inventories as $inventory){ foreach($inventories as $inventory){
$inventory->remove($targetItem); $inventory->remove($targetItem);
} }
@@ -135,14 +133,12 @@ class ClearCommand extends VanillaCommand{
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_clear_failure_no_items($target->getName())->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_clear_failure_no_items($target->getName())->prefix(TextFormat::RED));
} }
return true;
} }
/** /**
* @param Inventory[] $inventories * @param Inventory[] $inventories
*/ */
protected function countItems(array $inventories, ?Item $target) : int{ protected static function countItems(array $inventories, ?Item $target) : int{
$count = 0; $count = 0;
foreach($inventories as $inventory){ foreach($inventories as $inventory){
$contents = $target !== null ? $inventory->all($target) : $inventory->getContents(); $contents = $target !== null ? $inventory->all($target) : $inventory->getContents();

View File

@@ -25,14 +25,14 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable; use pocketmine\lang\Translatable;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function array_map; use function array_map;
use function array_shift;
use function count; use function count;
use function implode; use function implode;
use function is_array; use function is_array;
@@ -44,118 +44,123 @@ final class CommandAliasCommand extends Command{
private const LIST_PERM = DefaultPermissionNames::COMMAND_CMDALIAS_LIST; private const LIST_PERM = DefaultPermissionNames::COMMAND_CMDALIAS_LIST;
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
$overloads = [];
foreach([false, true] as $global){
$prefix = $global ? ["global"] : [];
$editPerm = $global ? self::GLOBAL_PERM : self::SELF_PERM;
$overloads[] = new CommandOverload(
[...$prefix, ...[
"create",
new StringParameter("alias", "alias"),
new StringParameter("target", "target")
]],
$editPerm,
fn(CommandSender $sender, string $alias, string $target) => $this->createAlias($sender, $alias, $target, $global)
);
$overloads[] = new CommandOverload(
[...$prefix, ...[
"delete",
new StringParameter("alias", "alias")
]],
$editPerm,
fn(CommandSender $sender, string $alias) => $this->deleteAlias($sender, $alias, $global)
);
$overloads[] = new CommandOverload(
[...$prefix, "list"],
self::LIST_PERM,
fn(CommandSender $sender) => $this->listAliases($sender, $global)
);
}
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
$overloads,
KnownTranslationFactory::pocketmine_command_cmdalias_description(), KnownTranslationFactory::pocketmine_command_cmdalias_description(),
"/cmdalias [global] create <alias> <target> OR /cmdalias [global] delete <alias>"
); );
$this->setPermissions([self::GLOBAL_PERM, self::SELF_PERM, self::LIST_PERM]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private function createAlias(CommandSender $sender, string $alias, string $target, bool $global) : void{
if(count($args) === 0){
throw new InvalidCommandSyntaxException();
}
$parsedArgs = $args;
$commandMap = $sender->getServer()->getCommandMap(); $commandMap = $sender->getServer()->getCommandMap();
if($parsedArgs[0] === "global"){ if($global){
$editPermission = self::GLOBAL_PERM;
$permissionCtx = $commandLabel . " global";
array_shift($parsedArgs);
$aliasMap = $commandMap->getAliasMap(); $aliasMap = $commandMap->getAliasMap();
$messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_global()); $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_global());
$auditLog = true; $auditLog = true;
}else{ }else{
$editPermission = self::SELF_PERM;
$permissionCtx = $commandLabel;
$aliasMap = $sender->getCommandAliasMap(); $aliasMap = $sender->getCommandAliasMap();
$messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_userSpecific()); $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_userSpecific());
$auditLog = false; $auditLog = false;
} }
$operation = array_shift($parsedArgs);
if($operation === "create"){ $command = $commandMap->getCommand($target, $sender->getCommandAliasMap());
if(count($parsedArgs) !== 2){ if($command === null){
throw new InvalidCommandSyntaxException(); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound(
} "/$target",
if(!$this->testPermission($permissionCtx, $sender, $editPermission)){ "/" . $sender->getCommandAliasMap()->getPreferredAlias("pocketmine:help", $sender->getServer()->getCommandMap()->getAliasMap())
return true; )->prefix(TextFormat::RED));
} return;
}
if(is_array($command)){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_error_aliasConflict("/$target", implode(", ", array_map(fn(Command $c) => "/" . $c->getId(), $command)))->prefix(TextFormat::RED));
return;
}
$aliasMap->bindAlias($command->getId(), $alias, override: true);
$message = $messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_create_success("/$alias", "/" . $command->getId()));
if($auditLog){
Command::broadcastCommandMessage($sender, $message);
}else{
$sender->sendMessage($message);
}
}
[$alias, $target] = $parsedArgs; private function deleteAlias(CommandSender $sender, string $alias, bool $global) : void{
$command = $commandMap->getCommand($target, $sender->getCommandAliasMap()); $commandMap = $sender->getServer()->getCommandMap();
if($command === null){ if($global){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound( $aliasMap = $commandMap->getAliasMap();
"/$target", $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_global());
"/" . $sender->getCommandAliasMap()->getPreferredAlias("pocketmine:help", $sender->getServer()->getCommandMap()->getAliasMap()) $auditLog = true;
)->prefix(TextFormat::RED)); }else{
return true; $aliasMap = $sender->getCommandAliasMap();
} $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_userSpecific());
if(is_array($command)){ $auditLog = false;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_error_aliasConflict("/$target", implode(", ", array_map(fn(Command $c) => "/" . $c->getId(), $command)))->prefix(TextFormat::RED)); }
return true; if($aliasMap->unbindAlias($alias)){
} $message = $messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_delete_success("/$alias"));
$aliasMap->bindAlias($command->getId(), $alias, override: true);
$message = $messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_create_success("/$alias", "/" . $command->getId()));
if($auditLog){ if($auditLog){
Command::broadcastCommandMessage($sender, $message); Command::broadcastCommandMessage($sender, $message);
}else{ }else{
$sender->sendMessage($message); $sender->sendMessage($message);
} }
return true; }else{
$sender->sendMessage($messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_delete_notFound("/$alias"))->prefix(TextFormat::RED));
} }
if($operation === "delete"){ }
if(count($parsedArgs) !== 1){
throw new InvalidCommandSyntaxException();
}
if(!$this->testPermission($permissionCtx, $sender, $editPermission)){
return true;
}
$alias = $parsedArgs[0]; private function listAliases(CommandSender $sender, bool $global) : void{
if($global){
if($aliasMap->unbindAlias($alias)){ $aliasMap = $sender->getServer()->getCommandMap()->getAliasMap();
$message = $messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_delete_success("/$alias")); $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_global());
if($auditLog){ }else{
Command::broadcastCommandMessage($sender, $message); $aliasMap = $sender->getCommandAliasMap();
}else{ $messageScope = fn(Translatable $t) => KnownTranslationFactory::pocketmine_command_cmdalias_template($t, KnownTranslationFactory::pocketmine_command_cmdalias_scope_userSpecific());
$sender->sendMessage($message); }
} $allAliases = $aliasMap->getAllAliases();
if(count($allAliases) === 0){
$sender->sendMessage($messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_list_noneSet())->prefix(TextFormat::RED));
return;
}
ksort($allAliases);
foreach(Utils::promoteKeys($allAliases) as $alias => $commandIds){
if(is_array($commandIds)){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_cmdalias_list_conflicted(
TextFormat::RED . "/$alias" . TextFormat::RESET,
implode(", ", array_map(fn(string $c) => TextFormat::RED . "/$c" . TextFormat::RESET, $commandIds))
));
}else{ }else{
$sender->sendMessage($messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_delete_notFound("/$alias"))->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_cmdalias_list_normal(
TextFormat::DARK_GREEN . "/$alias" . TextFormat::RESET,
TextFormat::DARK_GREEN . "/$commandIds" . TextFormat::RESET
));
} }
return true;
} }
if($operation === "list"){
if(count($parsedArgs) !== 0){
throw new InvalidCommandSyntaxException();
}
if(!$this->testPermission($permissionCtx, $sender, self::LIST_PERM)){
return true;
}
$allAliases = $aliasMap->getAllAliases();
if(count($allAliases) === 0){
$sender->sendMessage($messageScope(KnownTranslationFactory::pocketmine_command_cmdalias_list_noneSet())->prefix(TextFormat::RED));
return true;
}
ksort($allAliases);
foreach(Utils::promoteKeys($allAliases) as $alias => $commandIds){
if(is_array($commandIds)){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_cmdalias_list_conflicted(
TextFormat::RED . "/$alias" . TextFormat::RESET,
implode(", ", array_map(fn(string $c) => TextFormat::RED . "/$c" . TextFormat::RESET, $commandIds))
));
}else{
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_cmdalias_list_normal(
TextFormat::DARK_GREEN . "/$alias" . TextFormat::RESET,
TextFormat::DARK_GREEN . "/$commandIds" . TextFormat::RESET
));
}
}
return true;
}
throw new InvalidCommandSyntaxException();
} }
} }

View File

@@ -23,40 +23,35 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\GameMode; use pocketmine\player\GameMode;
use pocketmine\ServerProperties; use pocketmine\ServerProperties;
use function count;
class DefaultGamemodeCommand extends VanillaCommand{ class DefaultGamemodeCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new MappedParameter(
"gameMode",
"game mode",
static fn(string $v) : GameMode => GameMode::fromString($v) ?? throw new ParameterParseException("Illegal gamemode value")
)
], DefaultPermissionNames::COMMAND_DEFAULTGAMEMODE, self::execute(...))],
KnownTranslationFactory::pocketmine_command_defaultgamemode_description(), KnownTranslationFactory::pocketmine_command_defaultgamemode_description(),
KnownTranslationFactory::commands_defaultgamemode_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_DEFAULTGAMEMODE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ public function execute(CommandSender $sender, GameMode $gameMode) : void{
if(count($args) === 0){
throw new InvalidCommandSyntaxException();
}
$gameMode = GameMode::fromString($args[0]);
if($gameMode === null){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_unknown($args[0]));
return true;
}
//TODO: this probably shouldn't use the enum name directly
$sender->getServer()->getConfigGroup()->setConfigString(ServerProperties::GAME_MODE, $gameMode->name); $sender->getServer()->getConfigGroup()->setConfigString(ServerProperties::GAME_MODE, $gameMode->name);
$sender->sendMessage(KnownTranslationFactory::commands_defaultgamemode_success($gameMode->getTranslatableName())); $sender->sendMessage(KnownTranslationFactory::commands_defaultgamemode_success($gameMode->getTranslatableName()));
return true;
} }
} }

View File

@@ -25,42 +25,32 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
class DeopCommand extends VanillaCommand{ class DeopCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new StringParameter("playerName", "player name")],
DefaultPermissionNames::COMMAND_OP_TAKE,
self::execute(...))
],
KnownTranslationFactory::pocketmine_command_deop_description(), KnownTranslationFactory::pocketmine_command_deop_description(),
KnownTranslationFactory::commands_deop_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_OP_TAKE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $playerName) : void{
if(count($args) === 0){ $sender->getServer()->removeOp($playerName);
throw new InvalidCommandSyntaxException(); if(($player = $sender->getServer()->getPlayerExact($playerName)) !== null){
}
$name = array_shift($args);
if(!Player::isValidUserName($name)){
throw new InvalidCommandSyntaxException();
}
$sender->getServer()->removeOp($name);
if(($player = $sender->getServer()->getPlayerExact($name)) !== null){
$player->sendMessage(KnownTranslationFactory::commands_deop_message()->prefix(TextFormat::GRAY)); $player->sendMessage(KnownTranslationFactory::commands_deop_message()->prefix(TextFormat::GRAY));
} }
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_deop_success($name)); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_deop_success($playerName));
return true;
} }
} }

View File

@@ -25,49 +25,51 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\ServerProperties; use pocketmine\ServerProperties;
use pocketmine\world\World; use pocketmine\world\World;
use function count;
class DifficultyCommand extends VanillaCommand{ class DifficultyCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new MappedParameter(
"difficulty",
"difficulty",
static function(string $v) : int{
$difficulty = World::getDifficultyFromString($v);
if($difficulty === -1){
throw new ParameterParseException("Invalid difficulty value");
}
return $difficulty;
}
)],
DefaultPermissionNames::COMMAND_DIFFICULTY,
self::execute(...))
],
KnownTranslationFactory::pocketmine_command_difficulty_description(), KnownTranslationFactory::pocketmine_command_difficulty_description(),
KnownTranslationFactory::commands_difficulty_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_DIFFICULTY);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, int $difficulty) : void{
if(count($args) !== 1){
throw new InvalidCommandSyntaxException();
}
$difficulty = World::getDifficultyFromString($args[0]);
if($sender->getServer()->isHardcore()){ if($sender->getServer()->isHardcore()){
$difficulty = World::DIFFICULTY_HARD; $difficulty = World::DIFFICULTY_HARD;
} }
if($difficulty !== -1){ $sender->getServer()->getConfigGroup()->setConfigInt(ServerProperties::DIFFICULTY, $difficulty);
$sender->getServer()->getConfigGroup()->setConfigInt(ServerProperties::DIFFICULTY, $difficulty);
//TODO: add per-world support //TODO: add per-world support
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){ foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->setDifficulty($difficulty); $world->setDifficulty($difficulty);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_difficulty_success((string) $difficulty));
}else{
throw new InvalidCommandSyntaxException();
} }
return true; Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_difficulty_success((string) $difficulty));
} }
} }

View File

@@ -23,26 +23,31 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function date; use function date;
class DumpMemoryCommand extends VanillaCommand{ class DumpMemoryCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new RawParameter("path", "path")],
DefaultPermissionNames::COMMAND_DUMPMEMORY,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_dumpmemory_description(), KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
"/dumpmemory [path]"
); );
$this->setPermission(DefaultPermissionNames::COMMAND_DUMPMEMORY);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $path = "") : void{
$sender->getServer()->getMemoryManager()->dumpServerMemory($args[0] ?? (Path::join($sender->getServer()->getDataPath(), "memory_dumps", date("D_M_j-H.i.s-T_Y"))), 48, 80); $sender->getServer()->getMemoryManager()->dumpServerMemory($path !== "" ? $path : (Path::join($sender->getServer()->getDataPath(), "memory_dumps", date("D_M_j-H.i.s-T_Y"))), 48, 80);
return true;
} }
} }

View File

@@ -23,81 +23,84 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\BoolParameter;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\command\overload\StringParameter;
use pocketmine\entity\effect\Effect;
use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\StringToEffectParser; use pocketmine\entity\effect\StringToEffectParser;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\Limits; use pocketmine\utils\Limits;
use pocketmine\utils\TextFormat;
use function count; use function count;
use function strtolower;
class EffectCommand extends VanillaCommand{ class EffectCommand extends Command{
private const SELF_PERM = DefaultPermissionNames::COMMAND_EFFECT_OTHER;
private const OTHER_PERM = DefaultPermissionNames::COMMAND_EFFECT_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[
new CommandOverload([
//TODO: this should be a target param in the future
new StringParameter("target", "target"),
"clear"
//TODO: our permission system isn't granular enough for this right now - the permission required
//differs not by the usage, but by the target selected
], self::OVERLOAD_PERMS, self::removeEffect(...)),
new CommandOverload([
new StringParameter("target", "target"),
new MappedParameter("effect", "effect name", static fn(string $v) : Effect =>
StringToEffectParser::getInstance()->parse($v) ??
throw new ParameterParseException("Invalid effect name")
),
new IntRangeParameter("duration", "duration", 0, (int) (Limits::INT32_MAX / 20)),
new IntRangeParameter("amplifier", "amplifier", 0, 255),
new BoolParameter("bubbles", "bubbles")
//TODO: our permission system isn't granular enough for this right now - the permission required
//differs not by the usage, but by the target selected
], self::OVERLOAD_PERMS, self::modifyEffect(...))
],
KnownTranslationFactory::pocketmine_command_effect_description(), KnownTranslationFactory::pocketmine_command_effect_description(),
KnownTranslationFactory::commands_effect_usage()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_EFFECT_SELF,
DefaultPermissionNames::COMMAND_EFFECT_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function removeEffect(CommandSender $sender, string $target) : void{
if(count($args) < 2){ $player = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
if($player === null){ if($player === null){
return true; return;
} }
$effectManager = $player->getEffects(); $effectManager = $player->getEffects();
$effectManager->clear();
if(strtolower($args[1]) === "clear"){ $sender->sendMessage(KnownTranslationFactory::commands_effect_success_removed_all($player->getDisplayName()));
$effectManager->clear(); }
$sender->sendMessage(KnownTranslationFactory::commands_effect_success_removed_all($player->getDisplayName())); private function modifyEffect(
return true; CommandSender $sender,
} string $target,
Effect $effect,
$effect = StringToEffectParser::getInstance()->parse($args[1]); ?int $duration = null,
if($effect === null){ int $amplifier = 0,
$sender->sendMessage(KnownTranslationFactory::commands_effect_notFound($args[1])->prefix(TextFormat::RED)); bool $bubbles = true
return true; ) : void{
} $player = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
if($player === null){
$amplification = 0; return;
if(count($args) >= 3){
if(($d = $this->getBoundedInt($sender, $args[2], 0, (int) (Limits::INT32_MAX / 20))) === null){
return false;
}
$duration = $d * 20; //ticks
}else{
$duration = null;
}
if(count($args) >= 4){
$amplification = $this->getBoundedInt($sender, $args[3], 0, 255);
if($amplification === null){
return false;
}
}
$visible = true;
if(count($args) >= 5){
$v = strtolower($args[4]);
if($v === "on" || $v === "true" || $v === "t" || $v === "1"){
$visible = false;
}
} }
$effectManager = $player->getEffects();
if($duration === 0){ if($duration === 0){
if(!$effectManager->has($effect)){ if(!$effectManager->has($effect)){
@@ -106,17 +109,15 @@ class EffectCommand extends VanillaCommand{
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_effect_failure_notActive($effect->getName(), $player->getDisplayName())); $sender->sendMessage(KnownTranslationFactory::commands_effect_failure_notActive($effect->getName(), $player->getDisplayName()));
} }
return true; return;
} }
$effectManager->remove($effect); $effectManager->remove($effect);
$sender->sendMessage(KnownTranslationFactory::commands_effect_success_removed($effect->getName(), $player->getDisplayName())); $sender->sendMessage(KnownTranslationFactory::commands_effect_success_removed($effect->getName(), $player->getDisplayName()));
}else{ }else{
$instance = new EffectInstance($effect, $duration, $amplification, $visible); $instance = new EffectInstance($effect, $duration !== null ? $duration * 20 : null, $amplifier, $bubbles);
$effectManager->add($instance); $effectManager->add($instance);
self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_effect_success($effect->getName(), (string) $instance->getAmplifier(), $player->getDisplayName(), (string) ($instance->getDuration() / 20))); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_effect_success($effect->getName(), (string) $instance->getAmplifier(), $player->getDisplayName(), (string) ($instance->getDuration() / 20)));
} }
return true;
} }
} }

View File

@@ -23,59 +23,61 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\command\overload\StringParameter;
use pocketmine\item\enchantment\EnchantingHelper; use pocketmine\item\enchantment\EnchantingHelper;
use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\StringToEnchantmentParser; use pocketmine\item\enchantment\StringToEnchantmentParser;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function count; use pocketmine\utils\TextFormat;
class EnchantCommand extends VanillaCommand{ class EnchantCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_ENCHANT_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_ENCHANT_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_enchant_description(), [new CommandOverload([
KnownTranslationFactory::commands_enchant_usage() new StringParameter("target", "target"),
new MappedParameter("enchantment", "enchantment", static fn(string $v) : Enchantment => StringToEnchantmentParser::getInstance()->parse($v) ??
throw new ParameterParseException("Invalid enchantment name")
),
//sad, this one depends on previous parameters :(
new IntRangeParameter("level", "level", 1, 10)
], self::OVERLOAD_PERMS, self::enchant(...))],
KnownTranslationFactory::pocketmine_command_enchant_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_ENCHANT_SELF,
DefaultPermissionNames::COMMAND_ENCHANT_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private function enchant(CommandSender $sender, string $target, Enchantment $enchantment, int $level = 1) : void{
if(count($args) < 2){ $player = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
if($player === null){ if($player === null){
return true; return;
} }
$item = $player->getMainHandItem(); $item = $player->getMainHandItem();
if($item->isNull()){ if($item->isNull()){
$sender->sendMessage(KnownTranslationFactory::commands_enchant_noItem()); $sender->sendMessage(KnownTranslationFactory::commands_enchant_noItem());
return true; return;
} }
$enchantment = StringToEnchantmentParser::getInstance()->parse($args[1]); $max = $enchantment->getMaxLevel();
if($enchantment === null){ if($level > $max){
$sender->sendMessage(KnownTranslationFactory::commands_enchant_notFound($args[1])); $sender->sendMessage(KnownTranslationFactory::commands_generic_num_tooBig("$level", "$max")->prefix(TextFormat::RED));
return true; return;
}
$level = 1;
if(isset($args[2])){
$level = $this->getBoundedInt($sender, $args[2], 1, $enchantment->getMaxLevel());
if($level === null){
return false;
}
} }
//this is necessary to deal with enchanted books, which are a different item type than regular books //this is necessary to deal with enchanted books, which are a different item type than regular books
@@ -83,6 +85,5 @@ class EnchantCommand extends VanillaCommand{
$player->setMainHandItem($enchantedItem); $player->setMainHandItem($enchantedItem);
self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName())); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName()));
return true;
} }
} }

View File

@@ -25,46 +25,44 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\GameMode; use pocketmine\player\GameMode;
use function count;
class GamemodeCommand extends VanillaCommand{ class GamemodeCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_GAMEMODE_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_GAMEMODE_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new MappedParameter("gameMode", "game mode", static fn(string $v) : GameMode =>
GameMode::fromString($v) ?? throw new ParameterParseException("Invalid game mode: $v")
),
new StringParameter("target", "target")
], self::OVERLOAD_PERMS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_gamemode_description(), KnownTranslationFactory::pocketmine_command_gamemode_description(),
KnownTranslationFactory::commands_gamemode_usage()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_GAMEMODE_SELF,
DefaultPermissionNames::COMMAND_GAMEMODE_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, GameMode $gameMode, ?string $target = null) : void{
if(count($args) === 0){ $target = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$gameMode = GameMode::fromString($args[0]);
if($gameMode === null){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_unknown($args[0]));
return true;
}
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
if($target === null){ if($target === null){
return true; return;
} }
if($target->getGamemode() === $gameMode){ if($target->getGamemode() === $gameMode){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName())); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
return true; return;
} }
$target->setGamemode($gameMode); $target->setGamemode($gameMode);
@@ -78,7 +76,5 @@ class GamemodeCommand extends VanillaCommand{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_gamemode_success_other($gameMode->getTranslatableName(), $target->getName())); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_gamemode_success_other($gameMode->getTranslatableName(), $target->getName()));
} }
} }
return true;
} }
} }

View File

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
@@ -32,18 +34,18 @@ use function memory_get_usage;
use function number_format; use function number_format;
use function round; use function round;
class GarbageCollectorCommand extends VanillaCommand{ class GarbageCollectorCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_GC, self::execute(...))],
KnownTranslationFactory::pocketmine_command_gc_description() KnownTranslationFactory::pocketmine_command_gc_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_GC);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$chunksCollected = 0; $chunksCollected = 0;
$entitiesCollected = 0; $entitiesCollected = 0;
@@ -66,6 +68,5 @@ class GarbageCollectorCommand extends VanillaCommand{
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_cycles(TextFormat::RED . number_format($cyclesCollected))->prefix(TextFormat::GOLD)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_cycles(TextFormat::RED . number_format($cyclesCollected))->prefix(TextFormat::GOLD));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2))->prefix(TextFormat::GOLD)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gc_memoryFreed(TextFormat::RED . number_format(round((($memory - memory_get_usage()) / 1024) / 1024, 2), 2))->prefix(TextFormat::GOLD));
return true;
} }
} }

View File

@@ -25,7 +25,11 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\item\LegacyStringToItemParser; use pocketmine\item\LegacyStringToItemParser;
use pocketmine\item\LegacyStringToItemParserException; use pocketmine\item\LegacyStringToItemParserException;
use pocketmine\item\StringToItemParser; use pocketmine\item\StringToItemParser;
@@ -34,67 +38,54 @@ use pocketmine\nbt\JsonNbtParser;
use pocketmine\nbt\NbtDataException; use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\NbtException; use pocketmine\nbt\NbtException;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\TextFormat;
use function array_slice;
use function count;
use function implode;
class GiveCommand extends VanillaCommand{ class GiveCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_GIVE_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_GIVE_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new StringParameter("target", "target"),
new StringParameter("itemName", "item"),
new IntRangeParameter("count", "count", 1, 32767),
new RawParameter("nbt", "nbt")
], self::OVERLOAD_PERMS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_give_description(), KnownTranslationFactory::pocketmine_command_give_description(),
KnownTranslationFactory::pocketmine_command_give_usage()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_GIVE_SELF,
DefaultPermissionNames::COMMAND_GIVE_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $target, string $itemName, ?int $count = null, ?string $nbt = null) : void{
if(count($args) < 2){ $player = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
if($player === null){ if($player === null){
return true; return;
} }
try{ try{
$item = StringToItemParser::getInstance()->parse($args[1]) ?? LegacyStringToItemParser::getInstance()->parse($args[1]); $item = StringToItemParser::getInstance()->parse($itemName) ?? LegacyStringToItemParser::getInstance()->parse($itemName);
}catch(LegacyStringToItemParserException $e){ }catch(LegacyStringToItemParserException){
$sender->sendMessage(KnownTranslationFactory::commands_give_item_notFound($args[1])->prefix(TextFormat::RED)); throw new ParameterParseException("Invalid item name $itemName");
return true;
} }
$item->setCount($count ?? $item->getMaxStackSize());
if(!isset($args[2])){ if($nbt !== null){
$item->setCount($item->getMaxStackSize());
}else{
$count = $this->getBoundedInt($sender, $args[2], 1, 32767);
if($count === null){
return true;
}
$item->setCount($count);
}
if(isset($args[3])){
$data = implode(" ", array_slice($args, 3));
try{ try{
$tags = JsonNbtParser::parseJson($data); $tags = JsonNbtParser::parseJson($nbt);
}catch(NbtDataException $e){ }catch(NbtDataException $e){
$sender->sendMessage(KnownTranslationFactory::commands_give_tagError($e->getMessage())); $sender->sendMessage(KnownTranslationFactory::commands_give_tagError($e->getMessage()));
return true; return;
} }
try{ try{
$item->setNamedTag($tags); $item->setNamedTag($tags);
}catch(NbtException $e){ }catch(NbtException $e){
$sender->sendMessage(KnownTranslationFactory::commands_give_tagError($e->getMessage())); $sender->sendMessage(KnownTranslationFactory::commands_give_tagError($e->getMessage()));
return true; return;
} }
} }
@@ -102,10 +93,9 @@ class GiveCommand extends VanillaCommand{
$player->getInventory()->addItem($item); $player->getInventory()->addItem($item);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_give_success( Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_give_success(
$item->getName() . " (" . $args[1] . ")", $item->getName() . " ($itemName)",
(string) $item->getCount(), (string) $item->getCount(),
$player->getName() $player->getName()
)); ));
return true;
} }
} }

View File

@@ -25,6 +25,9 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\command\SimpleCommandMap; use pocketmine\command\SimpleCommandMap;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable; use pocketmine\lang\Translatable;
@@ -34,12 +37,10 @@ use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function array_chunk; use function array_chunk;
use function array_key_first; use function array_key_first;
use function array_pop;
use function count; use function count;
use function explode; use function explode;
use function implode; use function implode;
use function is_array; use function is_array;
use function is_numeric;
use function ksort; use function ksort;
use function min; use function min;
use function sort; use function sort;
@@ -48,98 +49,93 @@ use const PHP_INT_MAX;
use const SORT_FLAG_CASE; use const SORT_FLAG_CASE;
use const SORT_NATURAL; use const SORT_NATURAL;
class HelpCommand extends VanillaCommand{ class HelpCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[
new CommandOverload([
new IntRangeParameter("pageNumber", "page", 1, PHP_INT_MAX)
], DefaultPermissionNames::COMMAND_HELP, self::commandListPage(...)),
new CommandOverload([
new StringParameter("commandName", "command name")
], DefaultPermissionNames::COMMAND_HELP, self::commandSpecificInfo(...))
],
KnownTranslationFactory::pocketmine_command_help_description(), KnownTranslationFactory::pocketmine_command_help_description(),
KnownTranslationFactory::commands_help_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_HELP);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function commandListPage(CommandSender $sender, int $pageNumber = 1) : void{
if(count($args) === 0){
$commandName = "";
$pageNumber = 1;
}elseif(is_numeric($args[count($args) - 1])){
$pageNumber = (int) array_pop($args);
if($pageNumber <= 0){
$pageNumber = 1;
}
$commandName = implode(" ", $args);
}else{
$commandName = implode(" ", $args);
$pageNumber = 1;
}
$pageHeight = $sender->getScreenLineHeight(); $pageHeight = $sender->getScreenLineHeight();
//TODO: maybe inject this in the constructor instead of assuming the server's command map? //TODO: maybe inject this in the constructor instead of assuming the server's command map?
$commandMap = $sender->getServer()->getCommandMap(); $commandMap = $sender->getServer()->getCommandMap();
$userAliasMap = $sender->getCommandAliasMap(); $userAliasMap = $sender->getCommandAliasMap();
if($commandName === ""){ $commands = [];
$commands = []; foreach($commandMap->getUniqueCommands() as $command){
foreach($commandMap->getUniqueCommands() as $command){ if(count($command->getUsages($sender, "")) > 0){
if($command->testPermissionSilent($sender)){ $userAliases = $userAliasMap->getMergedAliases($command->getId(), $commandMap->getAliasMap());
$userAliases = $userAliasMap->getMergedAliases($command->getId(), $commandMap->getAliasMap()); $preferredAlias = $userAliases[array_key_first($userAliases)];
$preferredAlias = $userAliases[array_key_first($userAliases)]; if(isset($commands[$preferredAlias])){
if(isset($commands[$preferredAlias])){ throw new AssumptionFailedError("Something weird happened during user/global alias resolving");
throw new AssumptionFailedError("Something weird happened during user/global alias resolving");
}
$commands[$preferredAlias] = $command;
} }
$commands[$preferredAlias] = $command;
} }
ksort($commands, SORT_NATURAL | SORT_FLAG_CASE); }
$commands = array_chunk($commands, $pageHeight, preserve_keys: true); ksort($commands, SORT_NATURAL | SORT_FLAG_CASE);
$pageNumber = min(count($commands), $pageNumber); $commands = array_chunk($commands, $pageHeight, preserve_keys: true);
if($pageNumber < 1){ $pageNumber = min(count($commands), $pageNumber);
$pageNumber = 1; if($pageNumber < 1){
$pageNumber = 1;
}
$sender->sendMessage(KnownTranslationFactory::commands_help_header((string) $pageNumber, (string) count($commands)));
$lang = $sender->getLanguage();
if(isset($commands[$pageNumber - 1])){
foreach(Utils::promoteKeys($commands[$pageNumber - 1]) as $preferredAlias => $command){
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(TextFormat::DARK_GREEN . "/$preferredAlias: " . TextFormat::RESET . $descriptionString);
} }
$sender->sendMessage(KnownTranslationFactory::commands_help_header((string) $pageNumber, (string) count($commands)));
$lang = $sender->getLanguage();
if(isset($commands[$pageNumber - 1])){
foreach(Utils::promoteKeys($commands[$pageNumber - 1]) as $preferredAlias => $command){
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(TextFormat::DARK_GREEN . "/$preferredAlias: " . TextFormat::RESET . $descriptionString);
}
}
return true;
}else{
if(($command = $commandMap->getCommand(strtolower($commandName), $userAliasMap)) !== null){
if(is_array($command)){
SimpleCommandMap::handleConflicted($sender, $commandName, $command, $commandMap->getAliasMap());
return true;
}
if($command->testPermissionSilent($sender)){
$lang = $sender->getLanguage();
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($commandName)
->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------"));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString)
->prefix(TextFormat::GOLD));
$usage = $command->getUsage() ?? "/$commandName";
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
->prefix(TextFormat::GOLD));
$aliases = $userAliasMap->getMergedAliases($command->getId(), $commandMap->getAliasMap());
sort($aliases, SORT_NATURAL);
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::RESET . implode(", ", $aliases))
->prefix(TextFormat::GOLD));
return true;
}
}
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound($commandName, "/" . $userAliasMap->getPreferredAlias("pocketmine:help", $commandMap->getAliasMap()))->prefix(TextFormat::RED));
return true;
} }
} }
private static function commandSpecificInfo(CommandSender $sender, string $commandName) : void{
//TODO: maybe inject this in the constructor instead of assuming the server's command map?
$commandMap = $sender->getServer()->getCommandMap();
$userAliasMap = $sender->getCommandAliasMap();
if(($command = $commandMap->getCommand(strtolower($commandName), $userAliasMap)) !== null){
if(is_array($command)){
SimpleCommandMap::handleConflicted($sender, $commandName, $command, $commandMap->getAliasMap());
return;
}
$lang = $sender->getLanguage();
$usages = $command->getUsages($sender, $commandName);
if(count($usages) > 0){ //only permitted usages are shown
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($commandName)
->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------"));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString)
->prefix(TextFormat::GOLD));
$aliases = $userAliasMap->getMergedAliases($command->getId(), $commandMap->getAliasMap());
sort($aliases, SORT_NATURAL);
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::RESET . implode(", ", $aliases))
->prefix(TextFormat::GOLD));
foreach($usages as $usage){
$usageString = $lang->translate($usage);
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
->prefix(TextFormat::GOLD));
}
return;
}
}
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_notFound($commandName, "/" . $userAliasMap->getPreferredAlias("pocketmine:help", $commandMap->getAliasMap()))->prefix(TextFormat::RED));
}
} }

View File

@@ -25,37 +25,30 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
use function implode;
use function trim;
class KickCommand extends VanillaCommand{ class KickCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new StringParameter("playerName", "player name"),
new RawParameter("reason", "reason")
], DefaultPermissionNames::COMMAND_KICK, self::execute(...))],
KnownTranslationFactory::pocketmine_command_kick_description(), KnownTranslationFactory::pocketmine_command_kick_description(),
KnownTranslationFactory::commands_kick_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_KICK);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $playerName, string $reason = "") : void{
if(count($args) === 0){ if(($player = $sender->getServer()->getPlayerByPrefix($playerName)) instanceof Player){
throw new InvalidCommandSyntaxException();
}
$name = array_shift($args);
$reason = trim(implode(" ", $args));
if(($player = $sender->getServer()->getPlayerByPrefix($name)) instanceof Player){
$player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_kick($reason) : KnownTranslationFactory::pocketmine_disconnect_kick_noReason()); $player->kick($reason !== "" ? KnownTranslationFactory::pocketmine_disconnect_kick($reason) : KnownTranslationFactory::pocketmine_disconnect_kick_noReason());
if($reason !== ""){ if($reason !== ""){
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kick_success_reason($player->getName(), $reason)); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kick_success_reason($player->getName(), $reason));
@@ -65,7 +58,5 @@ class KickCommand extends VanillaCommand{
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
} }
return true;
} }
} }

View File

@@ -25,32 +25,34 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function count;
class KillCommand extends VanillaCommand{ class KillCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_KILL_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_KILL_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_kill_description(), [new CommandOverload([
KnownTranslationFactory::pocketmine_command_kill_usage() new StringParameter("target", "target")
], self::OVERLOAD_PERMS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_kill_description()
); );
$this->setPermissions([DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, ?string $target = null) : void{
if(count($args) >= 2){ $player = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
if($player === null){ if($player === null){
return true; return;
} }
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, $player->getHealth())); $player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, $player->getHealth()));
@@ -59,7 +61,5 @@ class KillCommand extends VanillaCommand{
}else{ }else{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName())); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_kill_successful($player->getName()));
} }
return true;
} }
} }

View File

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
@@ -34,18 +36,18 @@ use function implode;
use function sort; use function sort;
use const SORT_STRING; use const SORT_STRING;
class ListCommand extends VanillaCommand{ class ListCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_LIST, self::execute(...))],
KnownTranslationFactory::pocketmine_command_list_description() KnownTranslationFactory::pocketmine_command_list_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_LIST);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$playerNames = array_map(function(Player $player) : string{ $playerNames = array_map(function(Player $player) : string{
return $player->getName(); return $player->getName();
}, array_filter($sender->getServer()->getOnlinePlayers(), function(Player $player) use ($sender) : bool{ }, array_filter($sender->getServer()->getOnlinePlayers(), function(Player $player) use ($sender) : bool{
@@ -55,7 +57,5 @@ class ListCommand extends VanillaCommand{
$sender->sendMessage(KnownTranslationFactory::commands_players_list((string) count($playerNames), (string) $sender->getServer()->getMaxPlayers())); $sender->sendMessage(KnownTranslationFactory::commands_players_list((string) count($playerNames), (string) $sender->getServer()->getMaxPlayers()));
$sender->sendMessage(implode(", ", $playerNames)); $sender->sendMessage(implode(", ", $playerNames));
return true;
} }
} }

View File

@@ -23,34 +23,34 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function count;
use function implode;
class MeCommand extends VanillaCommand{ class MeCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new RawParameter("message", "message")],
DefaultPermissionNames::COMMAND_ME,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_me_description(), KnownTranslationFactory::pocketmine_command_me_description(),
KnownTranslationFactory::commands_me_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_ME);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $message) : void{
if(count($args) === 0){ $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_emote(
throw new InvalidCommandSyntaxException(); $sender instanceof Player ? $sender->getDisplayName() : $sender->getName(),
} TextFormat::RESET . $message
));
$sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args)));
return true;
} }
} }

View File

@@ -25,41 +25,38 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
class OpCommand extends VanillaCommand{ class OpCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new StringParameter("playerName", "player name")],
DefaultPermissionNames::COMMAND_OP_GIVE,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_op_description(), KnownTranslationFactory::pocketmine_command_op_description(),
KnownTranslationFactory::commands_op_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_OP_GIVE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $playerName) : void{
if(count($args) === 0){ if(!Player::isValidUserName($playerName)){
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
$name = array_shift($args); $sender->getServer()->addOp($playerName);
if(!Player::isValidUserName($name)){ if(($player = $sender->getServer()->getPlayerExact($playerName)) !== null){
throw new InvalidCommandSyntaxException();
}
$sender->getServer()->addOp($name);
if(($player = $sender->getServer()->getPlayerExact($name)) !== null){
$player->sendMessage(KnownTranslationFactory::commands_op_message()->prefix(TextFormat::GRAY)); $player->sendMessage(KnownTranslationFactory::commands_op_message()->prefix(TextFormat::GRAY));
} }
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_op_success($name)); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_op_success($playerName));
return true;
} }
} }

View File

@@ -25,32 +25,29 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function count;
class PardonCommand extends VanillaCommand{ class PardonCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload(
[new StringParameter("playerName", "player name")],
DefaultPermissionNames::COMMAND_UNBAN_PLAYER,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_unban_player_description(), KnownTranslationFactory::pocketmine_command_unban_player_description(),
KnownTranslationFactory::commands_unban_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_PLAYER);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $playerName) : void{
if(count($args) !== 1){ $sender->getServer()->getNameBans()->remove($playerName);
throw new InvalidCommandSyntaxException();
}
$sender->getServer()->getNameBans()->remove($args[0]); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_unban_success($playerName));
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_unban_success($args[0]));
return true;
} }
} }

View File

@@ -25,37 +25,34 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function count;
use function inet_pton; use function inet_pton;
class PardonIpCommand extends VanillaCommand{ class PardonIpCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_unban_ip_description(), [new CommandOverload(
KnownTranslationFactory::commands_unbanip_usage() [new StringParameter("ip", "IP address")],
DefaultPermissionNames::COMMAND_UNBAN_IP,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_unban_ip_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_IP);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $ip) : void{
if(count($args) !== 1){ if(inet_pton($ip) !== false){
throw new InvalidCommandSyntaxException(); $sender->getServer()->getIPBans()->remove($ip);
} $sender->getServer()->getNetwork()->unblockAddress($ip);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_unbanip_success($ip));
if(inet_pton($args[0]) !== false){
$sender->getServer()->getIPBans()->remove($args[0]);
$sender->getServer()->getNetwork()->unblockAddress($args[0]);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_unbanip_success($args[0]));
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_unbanip_invalid()); $sender->sendMessage(KnownTranslationFactory::commands_unbanip_invalid());
} }
return true;
} }
} }

View File

@@ -25,14 +25,21 @@ namespace pocketmine\command\defaults;
use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeIds;
use pocketmine\color\Color; use pocketmine\color\Color;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\FloatRangeParameter;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\RelativeFloat;
use pocketmine\command\overload\RelativeFloatParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\item\StringToItemParser; use pocketmine\item\StringToItemParser;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\utils\Random; use pocketmine\utils\Random;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\world\particle\AngryVillagerParticle; use pocketmine\world\particle\AngryVillagerParticle;
@@ -67,59 +74,71 @@ use pocketmine\world\particle\WaterParticle;
use pocketmine\world\World; use pocketmine\world\World;
use function count; use function count;
use function explode; use function explode;
use function max;
use function microtime; use function microtime;
use function mt_rand; use function mt_rand;
use function strtolower; use function strtolower;
class ParticleCommand extends VanillaCommand{ class ParticleCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_particle_description(), [new CommandOverload(
KnownTranslationFactory::pocketmine_command_particle_usage() [
new StringParameter("particleName", "particle name"),
new RelativeFloatParameter("x", "x"),
new RelativeFloatParameter("y", "y"),
new RelativeFloatParameter("z", "z"),
new FloatRangeParameter("xd", "xd", -1000, 1000),
new FloatRangeParameter("yd", "yd", -1000, 1000),
new FloatRangeParameter("zd", "zd", -1000, 1000),
new IntRangeParameter("count", "count", 1, 100000),
new StringParameter("data", "data")
],
DefaultPermissionNames::COMMAND_PARTICLE,
self::execute(...)
)],
KnownTranslationFactory::pocketmine_command_particle_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_PARTICLE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(
if(count($args) < 7){ CommandSender $sender,
throw new InvalidCommandSyntaxException(); string $particleName,
} RelativeFloat $x,
RelativeFloat $y,
RelativeFloat $z,
float $xd = 0.0,
float $yd = 0.0,
float $zd = 0.0,
int $count = 1,
string $data = ""
) : void{
if($sender instanceof Player){ if($sender instanceof Player){
$senderPos = $sender->getPosition(); $senderPos = $sender->getPosition();
$world = $senderPos->getWorld(); $world = $senderPos->getWorld();
$pos = new Vector3( [$senderX, $senderY, $senderZ] = [$senderPos->getX(), $senderPos->getY(), $senderPos->getZ()];
$this->getRelativeDouble($senderPos->getX(), $sender, $args[1]),
$this->getRelativeDouble($senderPos->getY(), $sender, $args[2], World::Y_MIN, World::Y_MAX),
$this->getRelativeDouble($senderPos->getZ(), $sender, $args[3])
);
}else{ }else{
$world = $sender->getServer()->getWorldManager()->getDefaultWorld(); $world = $sender->getServer()->getWorldManager()->getDefaultWorld();
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]); [$senderX, $senderY, $senderZ] = [0, 0, 0];
} }
$name = strtolower($args[0]); $pos = new Vector3(
$x->resolve($senderX, Limits::INT32_MIN, Limits::INT32_MAX),
$y->resolve($senderY, World::Y_MIN, World::Y_MAX),
$z->resolve($senderZ, Limits::INT32_MIN, Limits::INT32_MAX)
);
$xd = (float) $args[4]; $particle = self::getParticle(strtolower($particleName), $data);
$yd = (float) $args[5];
$zd = (float) $args[6];
$count = isset($args[7]) ? max(1, (int) $args[7]) : 1;
$data = $args[8] ?? null;
$particle = $this->getParticle($name, $data);
if($particle === null){ if($particle === null){
$sender->sendMessage(KnownTranslationFactory::commands_particle_notFound($name)->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_particle_notFound($particleName)->prefix(TextFormat::RED));
return true; return;
} }
$sender->sendMessage(KnownTranslationFactory::commands_particle_success($name, (string) $count)); $sender->sendMessage(KnownTranslationFactory::commands_particle_success($particleName, (string) $count));
$random = new Random((int) (microtime(true) * 1000) + mt_rand()); $random = new Random((int) (microtime(true) * 1000) + mt_rand());
@@ -130,11 +149,9 @@ class ParticleCommand extends VanillaCommand{
$random->nextSignedFloat() * $zd $random->nextSignedFloat() * $zd
), $particle); ), $particle);
} }
return true;
} }
private function getParticle(string $name, ?string $data = null) : ?Particle{ private static function getParticle(string $name, ?string $data = null) : ?Particle{
switch($name){ switch($name){
case "explode": case "explode":
return new ExplodeParticle(); return new ExplodeParticle();

View File

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\plugin\Plugin; use pocketmine\plugin\Plugin;
@@ -34,25 +36,23 @@ use function implode;
use function sort; use function sort;
use const SORT_STRING; use const SORT_STRING;
class PluginsCommand extends VanillaCommand{ class PluginsCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_PLUGINS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_plugins_description(), KnownTranslationFactory::pocketmine_command_plugins_description(),
null
); );
$this->setPermission(DefaultPermissionNames::COMMAND_PLUGINS);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$list = array_map(function(Plugin $plugin) : string{ $list = array_map(function(Plugin $plugin) : string{
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName(); return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
}, $sender->getServer()->getPluginManager()->getPlugins()); }, $sender->getServer()->getPluginManager()->getPlugins());
sort($list, SORT_STRING); sort($list, SORT_STRING);
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_plugins_success((string) count($list), implode(TextFormat::RESET . ", ", $list))); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_plugins_success((string) count($list), implode(TextFormat::RESET . ", ", $list)));
return true;
} }
} }

View File

@@ -25,23 +25,24 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function microtime; use function microtime;
use function round; use function round;
class SaveCommand extends VanillaCommand{ class SaveCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_SAVE_PERFORM, self::execute(...))],
KnownTranslationFactory::pocketmine_command_save_description() KnownTranslationFactory::pocketmine_command_save_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_PERFORM);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_save_start()); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_save_start());
$start = microtime(true); $start = microtime(true);
@@ -54,7 +55,5 @@ class SaveCommand extends VanillaCommand{
} }
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_save_success((string) round(microtime(true) - $start, 3))); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_save_success((string) round(microtime(true) - $start, 3)));
return true;
} }
} }

View File

@@ -25,25 +25,24 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
class SaveOffCommand extends VanillaCommand{ class SaveOffCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_SAVE_DISABLE, self::execute(...))],
KnownTranslationFactory::pocketmine_command_saveoff_description() KnownTranslationFactory::pocketmine_command_saveoff_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_DISABLE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$sender->getServer()->getWorldManager()->setAutoSave(false); $sender->getServer()->getWorldManager()->setAutoSave(false);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_save_disabled()); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_save_disabled());
return true;
} }
} }

View File

@@ -25,25 +25,24 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
class SaveOnCommand extends VanillaCommand{ class SaveOnCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_SAVE_ENABLE, self::execute(...))],
KnownTranslationFactory::pocketmine_command_saveon_description() KnownTranslationFactory::pocketmine_command_saveon_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_ENABLE);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$sender->getServer()->getWorldManager()->setAutoSave(true); $sender->getServer()->getWorldManager()->setAutoSave(true);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_save_enabled()); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_save_enabled());
return true;
} }
} }

View File

@@ -23,37 +23,33 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\console\ConsoleCommandSender; use pocketmine\console\ConsoleCommandSender;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function count;
use function implode;
class SayCommand extends VanillaCommand{ class SayCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new RawParameter("message", "message")
], DefaultPermissionNames::COMMAND_SAY, self::execute(...))],
KnownTranslationFactory::pocketmine_command_say_description(), KnownTranslationFactory::pocketmine_command_say_description(),
KnownTranslationFactory::commands_say_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SAY);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $message) : void{
if(count($args) === 0){
throw new InvalidCommandSyntaxException();
}
$sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_announcement( $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_announcement(
$sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()),
implode(" ", $args) $message
)->prefix(TextFormat::LIGHT_PURPLE)); )->prefix(TextFormat::LIGHT_PURPLE));
return true;
} }
} }

View File

@@ -23,30 +23,30 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
class SeedCommand extends VanillaCommand{ class SeedCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_SEED, self::execute(...))],
KnownTranslationFactory::pocketmine_command_seed_description() KnownTranslationFactory::pocketmine_command_seed_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SEED);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
if($sender instanceof Player){ if($sender instanceof Player){
$seed = $sender->getPosition()->getWorld()->getSeed(); $seed = $sender->getPosition()->getWorld()->getSeed();
}else{ }else{
$seed = $sender->getServer()->getWorldManager()->getDefaultWorld()->getSeed(); $seed = $sender->getServer()->getWorldManager()->getDefaultWorld()->getSeed();
} }
$sender->sendMessage(KnownTranslationFactory::commands_seed_success((string) $seed)); $sender->sendMessage(KnownTranslationFactory::commands_seed_success((string) $seed));
return true;
} }
} }

View File

@@ -25,59 +25,65 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RelativeFloat;
use pocketmine\command\overload\RelativeFloatParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\world\World; use pocketmine\world\World;
use function count;
class SetWorldSpawnCommand extends VanillaCommand{ class SetWorldSpawnCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_setworldspawn_description(), [
KnownTranslationFactory::commands_setworldspawn_usage() new CommandOverload([], DefaultPermissionNames::COMMAND_SETWORLDSPAWN, self::setSpawnHere(...)),
new CommandOverload([
new RelativeFloatParameter("x", "x"),
new RelativeFloatParameter("y", "y"),
new RelativeFloatParameter("z", "z")
], DefaultPermissionNames::COMMAND_SETWORLDSPAWN, self::setSpawnCoordinates(...))
],
KnownTranslationFactory::pocketmine_command_setworldspawn_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_SETWORLDSPAWN);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function setSpawnHere(CommandSender $sender) : void{
if(count($args) === 0){ if(!$sender instanceof Player){
if($sender instanceof Player){ $sender->sendMessage(TextFormat::RED . "You can only perform this command as a player");
$location = $sender->getPosition(); return;
$world = $location->getWorld();
$pos = $location->asVector3()->floor();
}else{
$sender->sendMessage(TextFormat::RED . "You can only perform this command as a player");
return true;
}
}elseif(count($args) === 3){
if($sender instanceof Player){
$base = $sender->getPosition();
$world = $base->getWorld();
}else{
$base = new Vector3(0.0, 0.0, 0.0);
$world = $sender->getServer()->getWorldManager()->getDefaultWorld();
}
$pos = (new Vector3(
$this->getRelativeDouble($base->x, $sender, $args[0]),
$this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX),
$this->getRelativeDouble($base->z, $sender, $args[2]),
))->floor();
}else{
throw new InvalidCommandSyntaxException();
} }
$location = $sender->getPosition();
$world = $location->getWorld();
$pos = $location->asVector3()->floor();
$world->setSpawnLocation($pos); $world->setSpawnLocation($pos);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_setworldspawn_success((string) $pos->x, (string) $pos->y, (string) $pos->z)); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_setworldspawn_success((string) $pos->x, (string) $pos->y, (string) $pos->z));
}
return true; private static function setSpawnCoordinates(CommandSender $sender, RelativeFloat $x, RelativeFloat $y, RelativeFloat $z) : void{
if($sender instanceof Player){
$base = $sender->getPosition();
$world = $base->getWorld();
}else{
$base = new Vector3(0.0, 0.0, 0.0);
$world = $sender->getServer()->getWorldManager()->getDefaultWorld();
}
$pos = (new Vector3(
$x->resolve($base->x, Limits::INT32_MIN, Limits::INT32_MAX),
$y->resolve($base->y, World::Y_MIN, World::Y_MAX),
$z->resolve($base->z, Limits::INT32_MIN, Limits::INT32_MAX)
))->floor();
$world->setSpawnLocation($pos);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_setworldspawn_success((string) $pos->x, (string) $pos->y, (string) $pos->z));
} }
} }

View File

@@ -25,56 +25,71 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RelativeFloat;
use pocketmine\command\overload\RelativeFloatParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\world\Position; use pocketmine\world\Position;
use pocketmine\world\World; use pocketmine\world\World;
use function count;
use function round; use function round;
class SpawnpointCommand extends VanillaCommand{ class SpawnpointCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_spawnpoint_description(), [
KnownTranslationFactory::commands_spawnpoint_usage() new CommandOverload([
new StringParameter("target", "target")
], self::OVERLOAD_PERMS, self::setSpawnHere(...)),
new CommandOverload([
new StringParameter("target", "target"),
new RelativeFloatParameter("x", "x"),
new RelativeFloatParameter("y", "y"),
new RelativeFloatParameter("z", "z")
], self::OVERLOAD_PERMS, self::setSpawnCoords(...))
],
KnownTranslationFactory::pocketmine_command_spawnpoint_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF,
DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function setSpawnHere(CommandSender $sender, ?string $target = null) : void{
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER); $target = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
if($target === null){ if($target === null){
return true; return;
} }
if(count($args) === 4){ $cpos = $target->getPosition();
$world = $target->getWorld(); $pos = Position::fromObject($cpos->floor(), $cpos->getWorld());
$pos = $sender instanceof Player ? $sender->getPosition() : $world->getSpawnLocation(); $target->setSpawn($pos);
$x = $this->getRelativeDouble($pos->x, $sender, $args[1]);
$y = $this->getRelativeDouble($pos->y, $sender, $args[2], World::Y_MIN, World::Y_MAX);
$z = $this->getRelativeDouble($pos->z, $sender, $args[3]);
$target->setSpawn(new Position($x, $y, $z, $world));
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2))); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
}
return true; private static function setSpawnCoords(CommandSender $sender, string $target, RelativeFloat $x, RelativeFloat $y, RelativeFloat $z) : void{
}elseif(count($args) <= 1 && $sender instanceof Player){ $target = self::fetchPermittedPlayerTarget($sender, $target, self::SELF_PERM, self::OTHER_PERM);
$cpos = $sender->getPosition(); if($target === null){
$pos = Position::fromObject($cpos->floor(), $cpos->getWorld()); return;
$target->setSpawn($pos);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($pos->x, 2), (string) round($pos->y, 2), (string) round($pos->z, 2)));
return true;
} }
throw new InvalidCommandSyntaxException(); $world = $target->getWorld();
$pos = $sender instanceof Player ? $sender->getPosition() : $world->getSpawnLocation();
$x = $x->resolve($pos->x, Limits::INT32_MIN, Limits::INT32_MAX);
$y = $y->resolve($pos->y, World::Y_MIN, World::Y_MAX);
$z = $z->resolve($pos->z, Limits::INT32_MIN, Limits::INT32_MAX);
$target->setSpawn(new Position($x, $y, $z, $world));
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_spawnpoint_success($target->getName(), (string) round($x, 2), (string) round($y, 2), (string) round($z, 2)));
} }
} }

View File

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\utils\Process; use pocketmine\utils\Process;
@@ -34,18 +36,18 @@ use function microtime;
use function number_format; use function number_format;
use function round; use function round;
class StatusCommand extends VanillaCommand{ class StatusCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_STATUS, self::execute(...))],
KnownTranslationFactory::pocketmine_command_status_description() KnownTranslationFactory::pocketmine_command_status_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_STATUS);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
$mUsage = Process::getAdvancedMemoryUsage(); $mUsage = Process::getAdvancedMemoryUsage();
$server = $sender->getServer(); $server = $sender->getServer();
@@ -113,7 +115,5 @@ class StatusCommand extends VanillaCommand{
"Time $timeColor" . round($world->getTickRateTime(), 2) . "ms" "Time $timeColor" . round($world->getTickRateTime(), 2) . "ms"
); );
} }
return true;
} }
} }

View File

@@ -25,25 +25,24 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
class StopCommand extends VanillaCommand{ class StopCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([], DefaultPermissionNames::COMMAND_STOP, self::execute(...))],
KnownTranslationFactory::pocketmine_command_stop_description() KnownTranslationFactory::pocketmine_command_stop_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_STOP);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender) : void{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_stop_start()); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_stop_start());
$sender->getServer()->shutdown(); $sender->getServer()->shutdown();
return true;
} }
} }

View File

@@ -25,100 +25,138 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\FloatRangeParameter;
use pocketmine\command\overload\RelativeFloat;
use pocketmine\command\overload\RelativeFloatParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\entity\Location; use pocketmine\entity\Location;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Limits;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\world\World; use pocketmine\world\World;
use function array_shift;
use function count;
use function round; use function round;
class TeleportCommand extends VanillaCommand{ class TeleportCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_tp_description(), [
KnownTranslationFactory::commands_tp_usage() new CommandOverload([
new RelativeFloatParameter("xIn", "x"),
new RelativeFloatParameter("yIn", "y"),
new RelativeFloatParameter("zIn", "z"),
new FloatRangeParameter("yaw", "yaw", 0, 360),
new FloatRangeParameter("pitch", "pitch", -90, 90)
], DefaultPermissionNames::COMMAND_TELEPORT_SELF, self::tpSelfCoords(...)),
new CommandOverload([
new StringParameter("teleportedPlayerName", "player to teleport"),
new RelativeFloatParameter("xIn", "x"),
new RelativeFloatParameter("yIn", "y"),
new RelativeFloatParameter("zIn", "z"),
new FloatRangeParameter("yaw", "yaw", 0, 360),
new FloatRangeParameter("pitch", "pitch", -90, 90)
], DefaultPermissionNames::COMMAND_TELEPORT_OTHER, self::tpOtherCoords(...)),
new CommandOverload([
new StringParameter("destinationPlayerName", "destination player")
], DefaultPermissionNames::COMMAND_TELEPORT_SELF, self::tpSelfToPlayer(...)),
new CommandOverload([
new StringParameter("teleportedPlayerName", "player to teleport"),
new StringParameter("destinationPlayerName", "destination player")
], DefaultPermissionNames::COMMAND_TELEPORT_OTHER, self::tpOtherToPlayer(...))
],
KnownTranslationFactory::pocketmine_command_tp_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_TELEPORT_SELF,
DefaultPermissionNames::COMMAND_TELEPORT_OTHER
]);
} }
private function findPlayer(CommandSender $sender, string $playerName) : ?Player{ private static function tpCoords(
$subject = $sender->getServer()->getPlayerByPrefix($playerName); CommandSender $sender,
if($subject === null){ Player $subject,
$sender->sendMessage(TextFormat::RED . "Can't find player " . $playerName); RelativeFloat $xIn,
return null; RelativeFloat $yIn,
} RelativeFloat $zIn,
return $subject; float $yaw,
float $pitch
) : void{
$base = $subject->getLocation();
$x = $xIn->resolve($base->x, Limits::INT32_MIN, Limits::INT32_MAX);
$y = $yIn->resolve($base->y, World::Y_MIN, World::Y_MAX);
$z = $zIn->resolve($base->z, Limits::INT32_MIN, Limits::INT32_MAX);
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
$subject->teleport($targetLocation);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success_coordinates(
$subject->getName(),
(string) round($targetLocation->x, 2),
(string) round($targetLocation->y, 2),
(string) round($targetLocation->z, 2)
));
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function tpSelfCoords(
switch(count($args)){ CommandSender $sender,
case 1: // /tp targetPlayer RelativeFloat $xIn,
case 3: // /tp x y z RelativeFloat $yIn,
case 5: // /tp x y z yaw pitch - TODO: 5 args could be target x y z yaw :( RelativeFloat $zIn,
$subjectName = null; //self float $yaw = 0.0,
break; float $pitch = 0.0
case 2: // /tp player1 player2 ) : void{
case 4: // /tp player1 x y z - TODO: 4 args could be x y z yaw :( if(!$sender instanceof Player){
case 6: // /tp player1 x y z yaw pitch throw new InvalidCommandSyntaxException("This syntax can only be used as a player");
$subjectName = array_shift($args);
break;
default:
throw new InvalidCommandSyntaxException();
} }
$subject = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER); self::tpCoords($sender, $sender, $xIn, $yIn, $zIn, $yaw, $pitch);
}
private static function tpOtherCoords(
CommandSender $sender,
string $teleportedPlayerName,
RelativeFloat $xIn,
RelativeFloat $yIn,
RelativeFloat $zIn,
float $yaw = 0.0,
float $pitch = 0.0
) : void{
$subject = self::fetchPermittedPlayerTarget($sender, $teleportedPlayerName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
if($subject === null){ if($subject === null){
return true; return;
} }
switch(count($args)){ self::tpCoords($sender, $subject, $xIn, $yIn, $zIn, $yaw, $pitch);
case 1: }
$targetPlayer = $this->findPlayer($sender, $args[0]);
if($targetPlayer === null){
return true;
}
$subject->teleport($targetPlayer->getLocation()); private static function tpToPlayer(CommandSender $sender, Player $teleportedPlayer, string $destinationPlayerName) : void{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success($subject->getName(), $targetPlayer->getName())); $destination = $sender->getServer()->getPlayerByPrefix($destinationPlayerName);
if($destination === null){
return true; //TODO: this isn't really a syntax error, but we don't have strings for it currently
case 3: $sender->sendMessage(TextFormat::RED . "Cannot find destination player: $destinationPlayerName");
case 5: return;
$base = $subject->getLocation();
if(count($args) === 5){
$yaw = (float) $args[3];
$pitch = (float) $args[4];
}else{
$yaw = $base->yaw;
$pitch = $base->pitch;
}
$x = $this->getRelativeDouble($base->x, $sender, $args[0]);
$y = $this->getRelativeDouble($base->y, $sender, $args[1], World::Y_MIN, World::Y_MAX);
$z = $this->getRelativeDouble($base->z, $sender, $args[2]);
$targetLocation = new Location($x, $y, $z, $base->getWorld(), $yaw, $pitch);
$subject->teleport($targetLocation);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success_coordinates(
$subject->getName(),
(string) round($targetLocation->x, 2),
(string) round($targetLocation->y, 2),
(string) round($targetLocation->z, 2)
));
return true;
default:
throw new AssumptionFailedError("This branch should be unreachable (for now)");
} }
$teleportedPlayer->teleport($destination->getLocation());
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_tp_success($teleportedPlayer->getName(), $destination->getName()));
}
private static function tpSelfToPlayer(CommandSender $sender, string $destinationPlayer) : void{
if(!$sender instanceof Player){
throw new InvalidCommandSyntaxException("This syntax can only be used as a player");
}
self::tpToPlayer($sender, $sender, $destinationPlayer);
}
private static function tpOtherToPlayer(CommandSender $sender, string $teleportedPlayerName, string $destinationPlayerName) : void{
$subject = self::fetchPermittedPlayerTarget($sender, $teleportedPlayerName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
if($subject === null){
return;
}
self::tpToPlayer($sender, $subject, $destinationPlayerName);
} }
} }

View File

@@ -25,41 +25,37 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
use function implode;
class TellCommand extends VanillaCommand{ class TellCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[new CommandOverload([
new StringParameter("recipientName", "recipient player"),
new RawParameter("message", "message")
], DefaultPermissionNames::COMMAND_TELL, self::execute(...))],
KnownTranslationFactory::pocketmine_command_tell_description(), KnownTranslationFactory::pocketmine_command_tell_description(),
KnownTranslationFactory::commands_message_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_TELL);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $recipientName, string $message) : void{
if(count($args) < 2){ $player = $sender->getServer()->getPlayerByPrefix($recipientName);
throw new InvalidCommandSyntaxException();
}
$player = $sender->getServer()->getPlayerByPrefix(array_shift($args));
if($player === $sender){ if($player === $sender){
$sender->sendMessage(KnownTranslationFactory::commands_message_sameTarget()->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_message_sameTarget()->prefix(TextFormat::RED));
return true; return;
} }
if($player instanceof Player){ if($player instanceof Player){
$message = implode(" ", $args);
$sender->sendMessage(KnownTranslationFactory::commands_message_display_outgoing($player->getDisplayName(), $message)->prefix(TextFormat::GRAY . TextFormat::ITALIC)); $sender->sendMessage(KnownTranslationFactory::commands_message_display_outgoing($player->getDisplayName(), $message)->prefix(TextFormat::GRAY . TextFormat::ITALIC));
$name = $sender instanceof Player ? $sender->getDisplayName() : $sender->getName(); $name = $sender instanceof Player ? $sender->getDisplayName() : $sender->getName();
$player->sendMessage(KnownTranslationFactory::commands_message_display_incoming($name, $message)->prefix(TextFormat::GRAY . TextFormat::ITALIC)); $player->sendMessage(KnownTranslationFactory::commands_message_display_incoming($name, $message)->prefix(TextFormat::GRAY . TextFormat::ITALIC));
@@ -67,7 +63,5 @@ class TellCommand extends VanillaCommand{
}else{ }else{
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()); $sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound());
} }
return true;
} }
} }

View File

@@ -25,119 +25,87 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\MappedParameter;
use pocketmine\command\overload\ParameterParseException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\world\World; use pocketmine\world\World;
use function count;
class TimeCommand extends VanillaCommand{ class TimeCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_time_description(), [
KnownTranslationFactory::pocketmine_command_time_usage() new CommandOverload(["start"], DefaultPermissionNames::COMMAND_TIME_START, self::startTime(...)),
new CommandOverload(["stop"], DefaultPermissionNames::COMMAND_TIME_STOP, self::stopTime(...)),
new CommandOverload(["query"], DefaultPermissionNames::COMMAND_TIME_QUERY, self::queryTime(...)),
new CommandOverload([
"set",
new MappedParameter("time", "time name", static fn(string $v) : int => match($v){
"day" => World::TIME_DAY,
"noon" => World::TIME_NOON,
"sunset" => World::TIME_SUNSET,
"night" => World::TIME_NIGHT,
"midnight" => World::TIME_MIDNIGHT,
"sunrise" => World::TIME_SUNRISE,
//numeric times are handled in a separate overload, for clarity's sake
default => throw new ParameterParseException("Invalid time name: $v")
})
], DefaultPermissionNames::COMMAND_TIME_SET, self::setTime(...)),
new CommandOverload([
"set",
new IntRangeParameter("time", "timestamp", 0, Limits::INT32_MAX)
], DefaultPermissionNames::COMMAND_TIME_SET, self::setTime(...)),
new CommandOverload([
"add",
new IntRangeParameter("ticks", "ticks", 0, Limits::INT32_MAX),
], DefaultPermissionNames::COMMAND_TIME_ADD, self::addTime(...))
],
KnownTranslationFactory::pocketmine_command_time_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_TIME_ADD,
DefaultPermissionNames::COMMAND_TIME_SET,
DefaultPermissionNames::COMMAND_TIME_START,
DefaultPermissionNames::COMMAND_TIME_STOP,
DefaultPermissionNames::COMMAND_TIME_QUERY
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function startTime(CommandSender $sender) : void{
if(count($args) < 1){ foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
throw new InvalidCommandSyntaxException(); $world->startTime();
} }
$testPermissionCtx = $commandLabel . " " . $args[0]; //TODO: l10n
Command::broadcastCommandMessage($sender, "Restarted the time");
}
if($args[0] === "start"){ private static function stopTime(CommandSender $sender) : void{
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_START)){ foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
return true; $world->stopTime();
}
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->startTime();
}
Command::broadcastCommandMessage($sender, "Restarted the time");
return true;
}elseif($args[0] === "stop"){
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_STOP)){
return true;
}
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->stopTime();
}
Command::broadcastCommandMessage($sender, "Stopped the time");
return true;
}elseif($args[0] === "query"){
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_QUERY)){
return true;
}
if($sender instanceof Player){
$world = $sender->getWorld();
}else{
$world = $sender->getServer()->getWorldManager()->getDefaultWorld();
}
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_time_query((string) $world->getTime())));
return true;
} }
Command::broadcastCommandMessage($sender, "Stopped the time");
}
if(count($args) < 2){ private static function queryTime(CommandSender $sender) : void{
throw new InvalidCommandSyntaxException(); if($sender instanceof Player){
} $world = $sender->getWorld();
if($args[0] === "set"){
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_SET)){
return true;
}
switch($args[1]){
case "day":
$value = World::TIME_DAY;
break;
case "noon":
$value = World::TIME_NOON;
break;
case "sunset":
$value = World::TIME_SUNSET;
break;
case "night":
$value = World::TIME_NIGHT;
break;
case "midnight":
$value = World::TIME_MIDNIGHT;
break;
case "sunrise":
$value = World::TIME_SUNRISE;
break;
default:
$value = $this->getInteger($sender, $args[1], 0);
break;
}
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->setTime($value);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_time_set((string) $value));
}elseif($args[0] === "add"){
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_ADD)){
return true;
}
$value = $this->getInteger($sender, $args[1], 0);
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->setTime($world->getTime() + $value);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_time_added((string) $value));
}else{ }else{
throw new InvalidCommandSyntaxException(); $world = $sender->getServer()->getWorldManager()->getDefaultWorld();
} }
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_time_query((string) $world->getTime())));
}
return true; private static function setTime(CommandSender $sender, int $time) : void{
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->setTime($time);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_time_set((string) $time));
}
private static function addTime(CommandSender $sender, int $ticks) : void{
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
$world->setTime($world->getTime() + $ticks);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_time_added((string) $ticks));
} }
} }

View File

@@ -25,7 +25,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
@@ -38,7 +38,6 @@ use pocketmine\utils\InternetException;
use pocketmine\utils\InternetRequestResult; use pocketmine\utils\InternetRequestResult;
use pocketmine\YmlServerProperties; use pocketmine\YmlServerProperties;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function count;
use function fclose; use function fclose;
use function file_exists; use function file_exists;
use function fopen; use function fopen;
@@ -50,7 +49,6 @@ use function is_int;
use function is_string; use function is_string;
use function json_decode; use function json_decode;
use function mkdir; use function mkdir;
use function strtolower;
use const CURLOPT_AUTOREFERER; use const CURLOPT_AUTOREFERER;
use const CURLOPT_FOLLOWLOCATION; use const CURLOPT_FOLLOWLOCATION;
use const CURLOPT_HTTPHEADER; use const CURLOPT_HTTPHEADER;
@@ -58,95 +56,79 @@ use const CURLOPT_POST;
use const CURLOPT_POSTFIELDS; use const CURLOPT_POSTFIELDS;
use const PHP_EOL; use const PHP_EOL;
class TimingsCommand extends VanillaCommand{ class TimingsCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[
new CommandOverload(["on"], DefaultPermissionNames::COMMAND_TIMINGS, self::enableTimings(...)),
new CommandOverload(["off"], DefaultPermissionNames::COMMAND_TIMINGS, self::disableTimings(...)),
new CommandOverload(["reset"], DefaultPermissionNames::COMMAND_TIMINGS, self::resetTimings(...)),
new CommandOverload(["paste"], DefaultPermissionNames::COMMAND_TIMINGS, self::requestTimingsUpload(...)),
new CommandOverload(["report"], DefaultPermissionNames::COMMAND_TIMINGS, self::requestTimingsFile(...))
],
KnownTranslationFactory::pocketmine_command_timings_description(), KnownTranslationFactory::pocketmine_command_timings_description(),
KnownTranslationFactory::pocketmine_command_timings_usage()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_TIMINGS);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function enableTimings(CommandSender $sender) : void{
if(count($args) !== 1){ if(TimingsHandler::isEnabled()){
throw new InvalidCommandSyntaxException(); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_alreadyEnabled());
return;
} }
TimingsHandler::setEnabled();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_enable());
}
$mode = strtolower($args[0]); private static function disableTimings(CommandSender $sender) : void{
TimingsHandler::setEnabled(false);
if($mode === "on"){ Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_disable());
if(TimingsHandler::isEnabled()){ }
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_alreadyEnabled());
return true;
}
TimingsHandler::setEnabled();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_enable());
return true;
}elseif($mode === "off"){
TimingsHandler::setEnabled(false);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_disable());
return true;
}
private static function resetTimings(CommandSender $sender) : void{
if(!TimingsHandler::isEnabled()){ if(!TimingsHandler::isEnabled()){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled()); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled());
return;
return true;
} }
$paste = $mode === "paste"; TimingsHandler::reload();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
}
if($mode === "reset"){ private static function requestTimingsUpload(CommandSender $sender) : void{
TimingsHandler::reload(); if(!TimingsHandler::isEnabled()){
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset()); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled());
}elseif($mode === "merged" || $mode === "report" || $paste){ return;
$timingsPromise = TimingsHandler::requestPrintTimings();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
$timingsPromise->onCompletion(
fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender),
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
);
}else{
throw new InvalidCommandSyntaxException();
} }
return true; $timingsPromise = TimingsHandler::requestPrintTimings();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
$timingsPromise->onCompletion(
fn(array $lines) => self::uploadReport($lines, $sender),
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
);
}
private static function requestTimingsFile(CommandSender $sender) : void{
if(!TimingsHandler::isEnabled()){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled());
return;
}
$timingsPromise = TimingsHandler::requestPrintTimings();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
$timingsPromise->onCompletion(
fn(array $lines) => self::createReportFile($lines, $sender),
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
);
} }
/** /**
* @param string[] $lines * @param string[] $lines
* @phpstan-param list<string> $lines * @phpstan-param list<string> $lines
*/ */
private function createReportFile(array $lines, CommandSender $sender) : void{ private static function uploadReport(array $lines, CommandSender $sender) : void{
$index = 0;
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
if(!file_exists($timingFolder)){
mkdir($timingFolder, 0777);
}
$timings = Path::join($timingFolder, "timings.txt");
while(file_exists($timings)){
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
}
$fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
foreach($lines as $line){
fwrite($fileTimings, $line . PHP_EOL);
}
fclose($fileTimings);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
}
/**
* @param string[] $lines
* @phpstan-param list<string> $lines
*/
private function uploadReport(array $lines, CommandSender $sender) : void{
$data = [ $data = [
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
"data" => implode("\n", $lines), "data" => implode("\n", $lines),
@@ -197,4 +179,29 @@ class TimingsCommand extends VanillaCommand{
} }
)); ));
} }
/**
* @param string[] $lines
* @phpstan-param list<string> $lines
*/
private static function createReportFile(array $lines, CommandSender $sender) : void{
$index = 0;
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
if(!file_exists($timingFolder)){
mkdir($timingFolder, 0777);
}
$timings = Path::join($timingFolder, "timings.txt");
while(file_exists($timings)){
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
}
$fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
foreach($lines as $line){
fwrite($fileTimings, $line . PHP_EOL);
}
fclose($fileTimings);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
}
} }

View File

@@ -23,80 +23,82 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\RawParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use function array_slice; use pocketmine\player\Player;
use function count; use pocketmine\utils\Limits;
use function implode;
class TitleCommand extends VanillaCommand{ class TitleCommand extends Command{
private const string SELF_PERM = DefaultPermissionNames::COMMAND_TITLE_SELF;
private const string OTHER_PERM = DefaultPermissionNames::COMMAND_TITLE_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
$playerParameter = new StringParameter("playerName", "player name");
$textParameter = new RawParameter("text", "text");
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
[
new CommandOverload([$playerParameter, "clear"], self::OVERLOAD_PERMS, self::clearTitles(...)),
new CommandOverload([$playerParameter, "reset"], self::OVERLOAD_PERMS, self::resetTitles(...)),
new CommandOverload([$playerParameter, "title", $textParameter], self::OVERLOAD_PERMS, self::sendTitle(...)),
new CommandOverload([$playerParameter, "subtitle", $textParameter], self::OVERLOAD_PERMS, self::sendSubTitle(...)),
new CommandOverload([$playerParameter, "actionbar", $textParameter], self::OVERLOAD_PERMS, self::sendActionBar(...)),
new CommandOverload([
$playerParameter,
"times",
new IntRangeParameter("fadeInTicks", "fade-in ticks", 0, Limits::INT32_MAX),
new IntRangeParameter("stayTicks", "stay ticks", 0, Limits::INT32_MAX),
new IntRangeParameter("fadeOutTicks", "fade-out ticks", 0, Limits::INT32_MAX)
], self::OVERLOAD_PERMS, self::setTitleDuration(...))
],
KnownTranslationFactory::pocketmine_command_title_description(), KnownTranslationFactory::pocketmine_command_title_description(),
KnownTranslationFactory::commands_title_usage()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_TITLE_SELF,
DefaultPermissionNames::COMMAND_TITLE_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ /**
if(count($args) < 2){ * @phpstan-param \Closure(Player) : void $action
throw new InvalidCommandSyntaxException(); */
} private static function doTitleAction(CommandSender $sender, string $playerName, \Closure $action) : void{
$player = self::fetchPermittedPlayerTarget($sender, $playerName, self::SELF_PERM, self::OTHER_PERM);
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
if($player === null){ if($player === null){
return true; return;
}
switch($args[1]){
case "clear":
$player->removeTitles();
break;
case "reset":
$player->resetTitles();
break;
case "title":
if(count($args) < 3){
throw new InvalidCommandSyntaxException();
}
$player->sendTitle(implode(" ", array_slice($args, 2)));
break;
case "subtitle":
if(count($args) < 3){
throw new InvalidCommandSyntaxException();
}
$player->sendSubTitle(implode(" ", array_slice($args, 2)));
break;
case "actionbar":
if(count($args) < 3){
throw new InvalidCommandSyntaxException();
}
$player->sendActionBarMessage(implode(" ", array_slice($args, 2)));
break;
case "times":
if(count($args) < 5){
throw new InvalidCommandSyntaxException();
}
$player->setTitleDuration($this->getInteger($sender, $args[2]), $this->getInteger($sender, $args[3]), $this->getInteger($sender, $args[4]));
break;
default:
throw new InvalidCommandSyntaxException();
} }
$action($player);
$sender->sendMessage(KnownTranslationFactory::commands_title_success()); $sender->sendMessage(KnownTranslationFactory::commands_title_success());
}
return true; private static function clearTitles(CommandSender $sender, string $playerName) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->removeTitles());
}
private static function resetTitles(CommandSender $sender, string $playerName) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->resetTitles());
}
private static function sendTitle(CommandSender $sender, string $playerName, string $text) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->sendTitle($text));
}
private static function sendSubTitle(CommandSender $sender, string $playerName, string $text) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->sendSubTitle($text));
}
private static function sendActionBar(CommandSender $sender, string $playerName, string $text) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->sendActionBarMessage($text));
}
private static function setTitleDuration(CommandSender $sender, string $playerName, int $fadeInTicks, int $stayTicks, int $fadeOutTicks) : void{
self::doTitleAction($sender, $playerName, static fn(Player $p) => $p->setTitleDuration($fadeInTicks, $stayTicks, $fadeOutTicks));
} }
} }

View File

@@ -23,36 +23,37 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player; use pocketmine\player\Player;
use function count; use pocketmine\utils\TextFormat;
class TransferServerCommand extends VanillaCommand{ class TransferServerCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_transferserver_description(), [new CommandOverload([
KnownTranslationFactory::pocketmine_command_transferserver_usage() new StringParameter("serverAddress", "server address"),
new IntRangeParameter("serverPort", "server port", 1, 65535)
], DefaultPermissionNames::COMMAND_TRANSFERSERVER, self::execute(...))],
KnownTranslationFactory::pocketmine_command_transferserver_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_TRANSFERSERVER);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function execute(CommandSender $sender, string $serverAddress, int $serverPort = 19132) : void{
if(count($args) < 1){ if(!($sender instanceof Player)){
throw new InvalidCommandSyntaxException(); $sender->sendMessage(TextFormat::RED . "This command must be executed as a player");
}elseif(!($sender instanceof Player)){
$sender->sendMessage("This command must be executed as a player");
return false; return;
} }
$sender->transfer($args[0], (int) ($args[1] ?? 19132)); $sender->transfer($serverAddress, $serverPort);
return true;
} }
} }

View File

@@ -1,121 +0,0 @@
<?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\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use function is_numeric;
use function substr;
abstract class VanillaCommand extends Command{
public const MAX_COORD = 30000000;
public const MIN_COORD = -30000000;
protected function fetchPermittedPlayerTarget(
string $testPermissionContext,
CommandSender $sender,
?string $target,
string $selfPermission,
string $otherPermission
) : ?Player{
if($target !== null){
$player = $sender->getServer()->getPlayerByPrefix($target);
}elseif($sender instanceof Player){
$player = $sender;
}else{
throw new InvalidCommandSyntaxException();
}
if($player === null){
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
return null;
}
//TODO: using loud testPermission here will generate misleading messages
//e.g. if the sender has self permission and tries to use the command on another player, it will give them a
//generic message saying that they don't have permission to use the command, which is not correct
if(
($player === $sender && $this->testPermission($testPermissionContext, $sender, $selfPermission)) ||
($player !== $sender && $this->testPermission($testPermissionContext, $sender, $otherPermission))
){
return $player;
}
return null;
}
protected function getInteger(CommandSender $sender, string $value, int $min = self::MIN_COORD, int $max = self::MAX_COORD) : int{
$i = (int) $value;
if($i < $min){
$i = $min;
}elseif($i > $max){
$i = $max;
}
return $i;
}
protected function getRelativeDouble(float $original, CommandSender $sender, string $input, float $min = self::MIN_COORD, float $max = self::MAX_COORD) : float{
if($input[0] === "~"){
$value = $this->getDouble($sender, substr($input, 1));
return $original + $value;
}
return $this->getDouble($sender, $input, $min, $max);
}
protected function getDouble(CommandSender $sender, string $value, float $min = self::MIN_COORD, float $max = self::MAX_COORD) : float{
$i = (double) $value;
if($i < $min){
$i = $min;
}elseif($i > $max){
$i = $max;
}
return $i;
}
protected function getBoundedInt(CommandSender $sender, string $input, int $min, int $max) : ?int{
if(!is_numeric($input)){
throw new InvalidCommandSyntaxException();
}
$v = (int) $input;
if($v > $max){
$sender->sendMessage(KnownTranslationFactory::commands_generic_num_tooBig($input, (string) $max)->prefix(TextFormat::RED));
return null;
}
if($v < $min){
$sender->sendMessage(KnownTranslationFactory::commands_generic_num_tooSmall($input, (string) $min)->prefix(TextFormat::RED));
return null;
}
return $v;
}
}

View File

@@ -23,7 +23,10 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
@@ -38,74 +41,77 @@ use function stripos;
use function strtolower; use function strtolower;
use const PHP_VERSION; use const PHP_VERSION;
class VersionCommand extends VanillaCommand{ class VersionCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_version_description(), [
KnownTranslationFactory::pocketmine_command_version_usage() //technically this could be one overload, but two lets us do two separate handlers
new CommandOverload([], DefaultPermissionNames::COMMAND_VERSION, self::showServerVersion(...)),
new CommandOverload([
new StringParameter("pluginName", "plugin name")
], DefaultPermissionNames::COMMAND_VERSION, self::showPluginVersion(...)),
],
KnownTranslationFactory::pocketmine_command_version_description()
); );
$this->setPermission(DefaultPermissionNames::COMMAND_VERSION);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function showServerVersion(CommandSender $sender) : void{
if(count($args) === 0){ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareName(
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareName( TextFormat::GREEN . VersionInfo::NAME . TextFormat::RESET
TextFormat::GREEN . VersionInfo::NAME . TextFormat::RESET ));
)); $versionColor = VersionInfo::IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : TextFormat::GREEN;
$versionColor = VersionInfo::IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : TextFormat::GREEN; $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareVersion(
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_serverSoftwareVersion( $versionColor . VersionInfo::VERSION()->getFullVersion() . TextFormat::RESET,
$versionColor . VersionInfo::VERSION()->getFullVersion() . TextFormat::RESET, TextFormat::GREEN . VersionInfo::GIT_HASH() . TextFormat::RESET
TextFormat::GREEN . VersionInfo::GIT_HASH() . TextFormat::RESET ));
)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_minecraftVersion(
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_minecraftVersion( TextFormat::GREEN . ProtocolInfo::MINECRAFT_VERSION_NETWORK . TextFormat::RESET,
TextFormat::GREEN . ProtocolInfo::MINECRAFT_VERSION_NETWORK . TextFormat::RESET, TextFormat::GREEN . ProtocolInfo::CURRENT_PROTOCOL . TextFormat::RESET
TextFormat::GREEN . ProtocolInfo::CURRENT_PROTOCOL . TextFormat::RESET ));
)); $sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpVersion(TextFormat::GREEN . PHP_VERSION . TextFormat::RESET));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpVersion(TextFormat::GREEN . PHP_VERSION . TextFormat::RESET));
$jitMode = Utils::getOpcacheJitMode(); $jitMode = Utils::getOpcacheJitMode();
if($jitMode !== null){ if($jitMode !== null){
if($jitMode !== 0){ if($jitMode !== 0){
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitEnabled(sprintf("CRTO: %d", $jitMode)); $jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitEnabled(sprintf("CRTO: %d", $jitMode));
}else{
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitDisabled();
}
}else{ }else{
$jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitNotSupported(); $jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitDisabled();
} }
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpJitStatus($jitStatus->format(TextFormat::GREEN, TextFormat::RESET)));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_operatingSystem(TextFormat::GREEN . Utils::getOS() . TextFormat::RESET));
}else{ }else{
$pluginName = implode(" ", $args); $jitStatus = KnownTranslationFactory::pocketmine_command_version_phpJitNotSupported();
$exactPlugin = $sender->getServer()->getPluginManager()->getPlugin($pluginName); }
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_phpJitStatus($jitStatus->format(TextFormat::GREEN, TextFormat::RESET)));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_operatingSystem(TextFormat::GREEN . Utils::getOS() . TextFormat::RESET));
}
if($exactPlugin instanceof Plugin){ private static function showPluginVersion(CommandSender $sender, string $pluginName) : void{
$this->describeToSender($exactPlugin, $sender); $exactPlugin = $sender->getServer()->getPluginManager()->getPlugin($pluginName);
return true; if($exactPlugin instanceof Plugin){
} self::describeToSender($exactPlugin, $sender);
$found = false; return;
$pluginName = strtolower($pluginName); }
foreach($sender->getServer()->getPluginManager()->getPlugins() as $plugin){
if(stripos($plugin->getName(), $pluginName) !== false){
$this->describeToSender($plugin, $sender);
$found = true;
}
}
if(!$found){ $found = false;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_noSuchPlugin()); $pluginName = strtolower($pluginName);
foreach($sender->getServer()->getPluginManager()->getPlugins() as $plugin){
if(stripos($plugin->getName(), $pluginName) !== false){
self::describeToSender($plugin, $sender);
$found = true;
} }
} }
return true; if(!$found){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_version_noSuchPlugin());
}
} }
private function describeToSender(Plugin $plugin, CommandSender $sender) : void{ private static function describeToSender(Plugin $plugin, CommandSender $sender) : void{
$desc = $plugin->getDescription(); $desc = $plugin->getDescription();
$sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::RESET . " version " . TextFormat::DARK_GREEN . $desc->getVersion()); $sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::RESET . " version " . TextFormat::DARK_GREEN . $desc->getVersion());

View File

@@ -25,6 +25,8 @@ namespace pocketmine\command\defaults;
use pocketmine\command\Command; use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\StringParameter;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
@@ -34,113 +36,84 @@ use pocketmine\ServerProperties;
use function count; use function count;
use function implode; use function implode;
use function sort; use function sort;
use function strtolower;
use const SORT_STRING; use const SORT_STRING;
class WhitelistCommand extends VanillaCommand{ class WhitelistCommand extends Command{
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_whitelist_description(), [
KnownTranslationFactory::commands_whitelist_usage() new CommandOverload(["reload"], DefaultPermissionNames::COMMAND_WHITELIST_RELOAD, self::reloadWhitelist(...)),
new CommandOverload(["enable"], DefaultPermissionNames::COMMAND_WHITELIST_ENABLE, self::enableWhitelist(...)),
new CommandOverload(["disable"], DefaultPermissionNames::COMMAND_WHITELIST_DISABLE, self::disableWhitelist(...)),
new CommandOverload(["list"], DefaultPermissionNames::COMMAND_WHITELIST_LIST, self::listWhitelist(...)),
new CommandOverload([
"add",
new StringParameter("playerName", "player name")
], DefaultPermissionNames::COMMAND_WHITELIST_ADD, self::addWhitelistedPlayer(...)),
new CommandOverload([
"remove",
new StringParameter("playerName", "player name"),
], DefaultPermissionNames::COMMAND_WHITELIST_REMOVE, self::removeWhitelistedPlayer(...))
],
KnownTranslationFactory::pocketmine_command_whitelist_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_WHITELIST_RELOAD,
DefaultPermissionNames::COMMAND_WHITELIST_ENABLE,
DefaultPermissionNames::COMMAND_WHITELIST_DISABLE,
DefaultPermissionNames::COMMAND_WHITELIST_LIST,
DefaultPermissionNames::COMMAND_WHITELIST_ADD,
DefaultPermissionNames::COMMAND_WHITELIST_REMOVE
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function reloadWhitelist(CommandSender $sender) : void{
if(count($args) === 0){ $server = $sender->getServer();
$server->getWhitelisted()->reload();
if($server->hasWhitelist()){
self::kickNonWhitelistedPlayers($server);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_reloaded());
}
private static function enableWhitelist(CommandSender $sender) : void{
$server = $sender->getServer();
$server->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, true);
self::kickNonWhitelistedPlayers($server);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_enabled());
}
private static function disableWhitelist(CommandSender $sender) : void{
$sender->getServer()->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, false);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_disabled());
}
private static function listWhitelist(CommandSender $sender) : void{
$entries = $sender->getServer()->getWhitelisted()->getAll(true);
sort($entries, SORT_STRING);
$result = implode(", ", $entries);
$count = (string) count($entries);
$sender->sendMessage(KnownTranslationFactory::commands_whitelist_list($count, $count));
$sender->sendMessage($result);
}
private static function addWhitelistedPlayer(CommandSender $sender, string $playerName) : void{
if(!Player::isValidUserName($playerName)){
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
$testPermissionCtx = $commandLabel . " " . $args[0]; $sender->getServer()->addWhitelist($playerName);
if(count($args) === 1){ Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_add_success($playerName));
switch(strtolower($args[0])){
case "reload":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_RELOAD)){
$server = $sender->getServer();
$server->getWhitelisted()->reload();
if($server->hasWhitelist()){
$this->kickNonWhitelistedPlayers($server);
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_reloaded());
}
return true;
case "on":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_ENABLE)){
$server = $sender->getServer();
$server->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, true);
$this->kickNonWhitelistedPlayers($server);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_enabled());
}
return true;
case "off":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_DISABLE)){
$sender->getServer()->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, false);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_disabled());
}
return true;
case "list":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_LIST)){
$entries = $sender->getServer()->getWhitelisted()->getAll(true);
sort($entries, SORT_STRING);
$result = implode(", ", $entries);
$count = (string) count($entries);
$sender->sendMessage(KnownTranslationFactory::commands_whitelist_list($count, $count));
$sender->sendMessage($result);
}
return true;
case "add":
$sender->sendMessage(KnownTranslationFactory::commands_generic_usage(KnownTranslationFactory::commands_whitelist_add_usage()));
return true;
case "remove":
$sender->sendMessage(KnownTranslationFactory::commands_generic_usage(KnownTranslationFactory::commands_whitelist_remove_usage()));
return true;
}
}elseif(count($args) === 2){
if(!Player::isValidUserName($args[1])){
throw new InvalidCommandSyntaxException();
}
switch(strtolower($args[0])){
case "add":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_ADD)){
$sender->getServer()->addWhitelist($args[1]);
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_add_success($args[1]));
}
return true;
case "remove":
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_REMOVE)){
$server = $sender->getServer();
$server->removeWhitelist($args[1]);
if(!$server->isWhitelisted($args[1])){
$server->getPlayerExact($args[1])?->kick(KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted()));
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_remove_success($args[1]));
}
return true;
}
}
throw new InvalidCommandSyntaxException();
} }
private function kickNonWhitelistedPlayers(Server $server) : void{ private static function removeWhitelistedPlayer(CommandSender $sender, string $playerName) : void{
if(!Player::isValidUserName($playerName)){
throw new InvalidCommandSyntaxException();
}
$server = $sender->getServer();
$server->removeWhitelist($playerName);
if(!$server->isWhitelisted($playerName)){
$server->getPlayerExact($playerName)?->kick(KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted()));
}
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_remove_success($playerName));
}
private static function kickNonWhitelistedPlayers(Server $server) : void{
$message = KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted()); $message = KnownTranslationFactory::pocketmine_disconnect_kick(KnownTranslationFactory::pocketmine_disconnect_whitelisted());
foreach($server->getOnlinePlayers() as $player){ foreach($server->getOnlinePlayers() as $player){
if(!$server->isWhitelisted($player->getName())){ if(!$server->isWhitelisted($player->getName())){

View File

@@ -23,8 +23,11 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\Command;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\command\overload\CommandOverload;
use pocketmine\command\overload\IntRangeParameter;
use pocketmine\command\overload\StringParameter;
use pocketmine\entity\Attribute; use pocketmine\entity\Attribute;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissionNames;
@@ -32,59 +35,66 @@ use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Limits; use pocketmine\utils\Limits;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use function abs; use function abs;
use function count; use function max;
use function str_ends_with; use function min;
use function substr;
class XpCommand extends VanillaCommand{ class XpCommand extends Command{
private const SELF_PERM = DefaultPermissionNames::COMMAND_XP_SELF;
private const OTHER_PERM = DefaultPermissionNames::COMMAND_XP_OTHER;
private const OVERLOAD_PERMS = [self::SELF_PERM, self::OTHER_PERM];
public function __construct(string $namespace, string $name){ public function __construct(string $namespace, string $name){
parent::__construct( parent::__construct(
$namespace, $namespace,
$name, $name,
KnownTranslationFactory::pocketmine_command_xp_description(), [
KnownTranslationFactory::pocketmine_command_xp_usage() new CommandOverload([
new IntRangeParameter("xp", "xp", 0, Limits::INT32_MAX),
new StringParameter("playerName", "player name"),
], self::OVERLOAD_PERMS, self::addXp(...)),
new CommandOverload([
new IntRangeParameter("xpLevels", "xp levels", Limits::INT32_MIN, Limits::INT32_MAX, "L"),
new StringParameter("playerName", "player name"),
], self::OVERLOAD_PERMS, self::addXpLevels(...))
],
KnownTranslationFactory::pocketmine_command_xp_description()
); );
$this->setPermissions([
DefaultPermissionNames::COMMAND_XP_SELF,
DefaultPermissionNames::COMMAND_XP_OTHER
]);
} }
public function execute(CommandSender $sender, string $commandLabel, array $args){ private static function addXp(CommandSender $sender, int $xp, ?string $playerName = null) : void{
if(count($args) < 1){ $player = self::fetchPermittedPlayerTarget($sender, $playerName, self::SELF_PERM, self::OTHER_PERM);
throw new InvalidCommandSyntaxException(); if($player === null){
return;
} }
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER); if($xp < 0){
$sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED));
}else{
$player->getXpManager()->addXp($xp, false);
$sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName()));
}
}
private static function addXpLevels(CommandSender $sender, int $xpLevels, ?string $playerName = null) : void{
$player = self::fetchPermittedPlayerTarget($sender, $playerName, self::SELF_PERM, self::OTHER_PERM);
if($player === null){ if($player === null){
return true; return;
} }
$xpManager = $player->getXpManager(); $xpManager = $player->getXpManager();
if(str_ends_with($args[0], "L")){ $xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError();
$xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError(); $maxXpLevel = (int) $xpLevelAttr->getMaxValue();
$maxXpLevel = (int) $xpLevelAttr->getMaxValue(); $currentXpLevel = $xpManager->getXpLevel();
$currentXpLevel = $xpManager->getXpLevel(); $xpLevels = max(-$currentXpLevel, min($maxXpLevel - $currentXpLevel, $xpLevels));
$xpLevels = $this->getInteger($sender, substr($args[0], 0, -1), -$currentXpLevel, $maxXpLevel - $currentXpLevel); if($xpLevels >= 0){
if($xpLevels >= 0){ $xpManager->addXpLevels($xpLevels, false);
$xpManager->addXpLevels($xpLevels, false); $sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName()));
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName()));
}else{
$xpLevels = abs($xpLevels);
$xpManager->subtractXpLevels($xpLevels);
$sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName()));
}
}else{ }else{
$xp = $this->getInteger($sender, $args[0], max: Limits::INT32_MAX); $xpLevels = abs($xpLevels);
if($xp < 0){ $xpManager->subtractXpLevels($xpLevels);
$sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED)); $sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName()));
}else{
$xpManager->addXp($xp, false);
$sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName()));
}
} }
return true;
} }
} }

View File

@@ -0,0 +1,48 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\lang\Translatable;
/**
* @phpstan-extends Parameter<bool>
*/
final class BoolParameter extends Parameter{
public function __construct(string $codeName, Translatable|string $printableName){
parent::__construct($codeName, $printableName, new NamedType(BuiltInType::BOOL));
}
public function parse(string $buffer, int &$offset) : bool{
$token = CommandStringHelper::parseQuoteAwareSingle($buffer, $offset);
return match($token){
"on", "true", "1" => true,
"off", "false", "0" => false,
default => throw new ParameterParseException("Invalid value for bool parameter: $token")
};
}
}

View File

@@ -0,0 +1,226 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\ParameterInfo;
use DaveRandom\CallbackValidator\Prototype;
use DaveRandom\CallbackValidator\ReturnInfo;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\Translatable;
use pocketmine\permission\PermissionManager;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function array_filter;
use function array_map;
use function count;
use function get_class;
use function implode;
use function is_string;
use function preg_match;
use function strlen;
use function strpos;
use function substr;
use function var_dump;
final class CommandOverload{
private int $requiredInputCount;
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $permissions;
/**
* @param Parameter[]|string[] $parameters
* @param string|string[] $permission
* @phpstan-param list<Parameter<*>|string> $parameters
* @phpstan-param string|list<string> $permission
* @phpstan-param anyClosure $handler
*/
public function __construct(
private array $parameters,
string|array $permission,
private \Closure $handler,
private bool $acceptsAliasUsed = false
){
$permissions = is_string($permission) ? [$permission] : $permission;
if(count($permissions) === 0){
throw new \InvalidArgumentException("At least one permission must be provided");
}
$permissionManager = PermissionManager::getInstance();
foreach($permissions as $perm){
if($permissionManager->getPermission($perm) === null){
throw new \InvalidArgumentException("Cannot use non-existing permission \"$perm\"");
}
}
$this->permissions = $permissions;
//TODO: auto infer parameter infos if they aren't provided?
//TODO: allow the type of CommandSender to be constrained - this can be useful for player-only commands etc
$alwaysPresentArgs = self::alwaysPresentArgs($this->acceptsAliasUsed);
$passableParameters = array_filter($this->parameters, is_object(...));
$expectedPrototype = new Prototype(
new ReturnInfo(new NamedType(BuiltInType::VOID), byReference: false),
...$alwaysPresentArgs,
...array_map(fn(Parameter $p) => new ParameterInfo(
$p->getCodeName(),
$p->getCodeType(),
byReference: false,
isOptional: false,
isVariadic: false,
), $passableParameters)
);
$actualPrototype = Prototype::fromClosure($this->handler);
Utils::validateCallableSignature($expectedPrototype, $actualPrototype);
$this->requiredInputCount = $actualPrototype->getRequiredParameterCount() - count($alwaysPresentArgs);
}
/**
* @return ParameterInfo[]
*/
private static function alwaysPresentArgs(bool $acceptsAliasUsed) : array{
$result = [new ParameterInfo("sender", new NamedType(CommandSender::class), byReference: false, isOptional: false, isVariadic: false)];
if($acceptsAliasUsed){
$result[] = new ParameterInfo("aliasUsed", new NamedType(BuiltInType::STRING), byReference: false, isOptional: false, isVariadic: false);
}
return $result;
}
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getPermissions() : array{ return $this->permissions; }
public function senderHasAnyPermissions(CommandSender $sender) : bool{
foreach($this->permissions as $permission){
if($sender->hasPermission($permission)){
return true;
}
}
return false;
}
public function getUsage() : Translatable{
$templates = [];
$args = [];
$pos = 0;
foreach($this->parameters as $parameter){
if(is_string($parameter)){
//literal token
$templates[] = $parameter;
continue;
}
//TODO: printable type info would be nice
if($pos < $this->requiredInputCount){
$template = "<{%$pos}>";
}else{
$template = "[{%$pos}]";
}
$suffix = $parameter->getSuffix();
$template .= $suffix;
$templates[] = $template;
$args[] = $parameter->getPrintableName();
$pos++;
}
return new Translatable(implode(" ", $templates), $args);
}
private static function skipWhitespace(string $commandLine, int &$offset) : int{
if(preg_match('/\G\s+/', $commandLine, $matches, offset: $offset) > 0){
$offset += strlen($matches[0]);
return strlen($matches[0]);
}
return 0;
}
/**
* @throws InvalidCommandSyntaxException
*/
public function invoke(CommandSender $sender, string $aliasUsed, string $commandLine) : void{
$offset = 0;
$args = [];
//skip preceding whitespace
self::skipWhitespace($commandLine, $offset);
if($offset < strlen($commandLine)){
foreach($this->parameters as $parameter){
if(is_string($parameter)){
if(strpos($commandLine, $parameter, $offset) === $offset){
$offset += strlen($parameter);
}else{
throw new ParameterParseException("Literal \"$parameter\" expected");
}
}else{
try{
$args[] = $parameter->parse($commandLine, $offset);
}catch(ParameterParseException $e){
throw new ParameterParseException(
"Failed parsing argument \$" . $parameter->getCodeName() . ": " . $e->getMessage(),
previous: $e
);
}
}
if(self::skipWhitespace($commandLine, $offset) === 0){
if($offset === strlen($commandLine)){
//no more tokens, rest of the parameters must be optional
break;
}else{
if(is_string($parameter)){
throw new AssumptionFailedError();
}
var_dump(substr($commandLine, $offset));
throw new ParameterParseException("Parameter " . get_class($parameter) . " for \$" . $parameter->getCodeName() . " didn't stop on a whitespace character");
}
}
}
}
if($offset !== strlen($commandLine)){
throw new InvalidCommandSyntaxException("Too many arguments provided for overload");
}
if(count($args) < $this->requiredInputCount){
throw new InvalidCommandSyntaxException("Not enough arguments provided for overload");
}
//Reflection magic here :)
//TODO: maybe we don't want to invoke this directly, but hand the args back to the caller?
//this would allow resolving by more than just overload order
if($this->acceptsAliasUsed){
// @phpstan-ignore-next-line
($this->handler)($sender, $aliasUsed, ...$args);
}else{
// @phpstan-ignore-next-line
($this->handler)($sender, ...$args);
}
}
}

View File

@@ -0,0 +1,61 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\lang\Translatable;
use function preg_match;
use function strlen;
/**
* @phpstan-extends Parameter<float>
*/
final class FloatRangeParameter extends Parameter{
public function __construct(
string $codeName,
Translatable|string $printableName,
private float $min,
private float $max
){
parent::__construct($codeName, $printableName, new NamedType(BuiltInType::FLOAT));
}
public function parse(string $buffer, int &$offset) : float{
if(preg_match('/\G(-?\d+\.?\d*)/', $buffer, $matches, offset: $offset) > 0){
$offset += strlen($matches[0]);
$value = (float) $matches[0];
if($value < $this->min || $value > $this->max){
//TODO: we should probably use localised messages for this, but they probably won't be seen by the user
//anyway since we'll try all the overloads before giving up
throw new ParameterParseException("Value must be in the range $this->min ... $this->max");
}
return $value;
}
throw new ParameterParseException("Expected a float in the range $this->min ... $this->max");
}
}

View File

@@ -0,0 +1,67 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\lang\Translatable;
use function preg_match;
use function preg_quote;
use function strlen;
/**
* @phpstan-extends Parameter<int>
*/
final class IntRangeParameter extends Parameter{
public function __construct(
string $codeName,
Translatable|string $printableName,
private int $min,
private int $max,
private string $suffix = ""
){
parent::__construct($codeName, $printableName, new NamedType(BuiltInType::INT));
}
public function parse(string $buffer, int &$offset) : int{
if(preg_match('/\G-?\d+' . preg_quote($this->suffix, '/') . '/', $buffer, $matches, offset: $offset) > 0){
$offset += strlen($matches[0]);
$int = (int) $matches[0];
if($int < $this->min || $int > $this->max){
//TODO: we should probably use localised messages for this, but they probably won't be seen by the user
//anyway since we'll try all the overloads before giving up
throw new ParameterParseException("Value must be in the range $this->min ... $this->max");
}
return $int;
}
throw new ParameterParseException("Expected an integer in the range $this->min ... $this->max");
}
public function getSuffix() : string{
return $this->suffix;
}
}

View File

@@ -0,0 +1,75 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\ParameterInfo;
use DaveRandom\CallbackValidator\Prototype;
use DaveRandom\CallbackValidator\ReturnInfo;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\lang\Translatable;
use pocketmine\utils\Utils;
/**
* Looks up a string and converts it to some type of value.
* The associated argument type will be inferred from the return type of the provided mapper function.
*
* @phpstan-template TValue
* @phpstan-extends Parameter<TValue>
*/
final class MappedParameter extends Parameter{
/**
* @phpstan-param \Closure(string): TValue $mapper
*/
public function __construct(
string $codeName,
Translatable|string $printableName,
private \Closure $mapper
){
$givenPrototype = Prototype::fromClosure($this->mapper);
$type = $givenPrototype->getReturnInfo()->type;
if($type === null){
throw new \InvalidArgumentException("Mapper callback must have a return type set");
}
$expectedPrototype = new Prototype(
new ReturnInfo($type, byReference: false),
new ParameterInfo("value", new NamedType(BuiltInType::STRING), byReference: false, isOptional: false, isVariadic: false)
);
Utils::validateCallableSignature($expectedPrototype, $givenPrototype);
parent::__construct(
$codeName,
$printableName,
$type
);
}
public function parse(string $buffer, int &$offset) : mixed{
$lookupKey = CommandStringHelper::parseQuoteAwareSingle($buffer, $offset) ?? throw new ParameterParseException("Unable to parse an argument from the buffer");
return ($this->mapper)($lookupKey);
}
}

View File

@@ -0,0 +1,70 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BaseType;
use pocketmine\lang\Translatable;
/**
* @phpstan-template TValue
*/
abstract class Parameter{
public function __construct(
private string $codeName,
private Translatable|string $printableName,
private BaseType $codeType,
){}
public function getCodeName() : string{
return $this->codeName;
}
public function getPrintableName() : Translatable|string{
return $this->printableName;
}
public function getCodeType() : BaseType{
return $this->codeType;
}
/**
* Returns whether this command will consume all remaining inputs.
*/
public function consumesAllRemainingInputs() : bool{
return false;
}
/**
* The given string will be stripped of whitespace at the start
*
* @phpstan-return TValue
* @throws ParameterParseException
*/
abstract public function parse(string $buffer, int &$offset) : mixed;
public function getSuffix() : string{
return "";
}
}

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\command\overload;
use pocketmine\command\utils\InvalidCommandSyntaxException;
final class ParameterParseException extends InvalidCommandSyntaxException{
}

View File

@@ -0,0 +1,54 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\lang\Translatable;
use function strlen;
use function substr;
/**
* @phpstan-extends Parameter<string>
*/
final class RawParameter extends Parameter{
public function __construct(string $codeName, Translatable|string $printableName){
parent::__construct(
$codeName,
$printableName,
new NamedType(BuiltInType::STRING)
);
}
public function consumesAllRemainingInputs() : bool{
return true;
}
public function parse(string $buffer, int &$offset) : string{
$value = substr($buffer, $offset);
$offset += strlen($value);
return $value;
}
}

View File

@@ -0,0 +1,44 @@
<?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\command\overload;
use function max;
use function min;
final class RelativeFloat{
public function __construct(
private float $value,
private bool $relative
){}
public function getValue() : float{ return $this->value; }
public function isRelative() : bool{ return $this->relative; }
public function resolve(float $base, float $min, float $max) : float{
//TODO: this should probably bail on out of bounds values
return min($max, max($min, $this->relative ? $base + $this->value : $this->value));
}
}

View File

@@ -0,0 +1,54 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\lang\Translatable;
use function preg_match;
use function strlen;
/**
* @phpstan-extends Parameter<RelativeFloat>
*/
final class RelativeFloatParameter extends Parameter{
public function __construct(string $codeName, Translatable|string $printableName){
parent::__construct($codeName, $printableName, new NamedType(RelativeFloat::class));
}
public function parse(string $buffer, int &$offset) : RelativeFloat{
if(preg_match('/\G(~)?(-?\d+\.?\d*)?/', $buffer, $matches, offset: $offset) > 0){
$relativeRaw = $matches[1] ?? "";
$valueRaw = $matches[2] ?? "";
if($valueRaw !== "" || $relativeRaw !== ""){
$offset += strlen($matches[0]);
$relative = $relativeRaw === "~";
$value = (float) $valueRaw;
return new RelativeFloat($value, $relative);
}
}
throw new ParameterParseException("Expected a float, possibly preceded by a ~ symbol");
}
}

View File

@@ -0,0 +1,50 @@
<?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\command\overload;
use DaveRandom\CallbackValidator\Type\BuiltInType;
use DaveRandom\CallbackValidator\Type\NamedType;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\lang\Translatable;
/**
* @phpstan-extends Parameter<string>
*/
final class StringParameter extends Parameter{
public function __construct(
string $codeName,
Translatable|string $printableName,
){
parent::__construct(
$codeName,
$printableName,
new NamedType(BuiltInType::STRING)
);
}
public function parse(string $buffer, int &$offset) : string{
return CommandStringHelper::parseQuoteAwareSingle($buffer, $offset) ?? throw new ParameterParseException();
}
}

View File

@@ -25,8 +25,10 @@ namespace pocketmine\command\utils;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use function preg_last_error_msg; use function preg_last_error_msg;
use function preg_match;
use function preg_match_all; use function preg_match_all;
use function preg_replace; use function preg_replace;
use function strlen;
final class CommandStringHelper{ final class CommandStringHelper{
@@ -60,4 +62,30 @@ final class CommandStringHelper{
return $args; return $args;
} }
/**
* Splits by the same logic as {@link self::parseQuoteAware()}, but doesn't strip quotes from the parts or remove
* escapes. Useful if you need to join the parts back into a new command string.
*
* @return string[]
*/
public static function splitQuoteAware(string $commandLine) : array{
preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|(\S+)/u', $commandLine, $matches);
return $matches[0];
}
public static function parseQuoteAwareSingle(string $commandLine, int &$offset = 0) : ?string{
//quoted or bare string, like the old CommandStringHelper
if(preg_match('/\G(?:"((?:\\\\.|[^\\\\"])*)"|(\S+))/u', $commandLine, $matches, offset: $offset) > 0){
$offset += strlen($matches[0]);
for($i = 1; $i <= 2; ++$i){
if($matches[$i] !== ""){
$match = $matches[$i];
return preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
}
}
}
return null;
}
} }

View File

@@ -1099,7 +1099,8 @@ class NetworkSession{
$globalAliasMap = $this->server->getCommandMap()->getAliasMap(); $globalAliasMap = $this->server->getCommandMap()->getAliasMap();
$userAliasMap = $this->player->getCommandAliasMap(); $userAliasMap = $this->player->getCommandAliasMap();
foreach($this->server->getCommandMap()->getUniqueCommands() as $command){ foreach($this->server->getCommandMap()->getUniqueCommands() as $command){
if(!$command->testPermissionSilent($this->player)){ if(count($command->getUsages($this->player, "")) === 0){
//no permitted overloads
continue; continue;
} }

View File

@@ -559,9 +559,9 @@ final class Utils{
* @phpstan-param anyClosure $signature * @phpstan-param anyClosure $signature
* @phpstan-param anyClosure $subject * @phpstan-param anyClosure $subject
*/ */
public static function validateCallableSignature(Prototype|\Closure $signature, \Closure $subject) : void{ public static function validateCallableSignature(Prototype|\Closure $signature, Prototype|\Closure $subject) : void{
$signaturePrototype = $signature instanceof Prototype ? $signature : Prototype::fromClosure($signature); $signaturePrototype = $signature instanceof Prototype ? $signature : Prototype::fromClosure($signature);
$subjectPrototype = Prototype::fromClosure($subject); $subjectPrototype = $subject instanceof Prototype ? $subject : Prototype::fromClosure($subject);
if(!$signaturePrototype->isSatisfiedBy($subjectPrototype)){ if(!$signaturePrototype->isSatisfiedBy($subjectPrototype)){
throw new \TypeError("Declaration of callable `$subjectPrototype` must be compatible with `$signaturePrototype`"); throw new \TypeError("Declaration of callable `$subjectPrototype` must be compatible with `$signaturePrototype`");
} }

View File

@@ -781,7 +781,7 @@ parameters:
path: ../../../src/network/mcpe/NetworkSession.php path: ../../../src/network/mcpe/NetworkSession.php
- -
message: '#^Parameter \#1 \$target of method pocketmine\\command\\Command\:\:testPermissionSilent\(\) expects pocketmine\\command\\CommandSender, pocketmine\\player\\Player\|null given\.$#' message: '#^Parameter \#1 \$sender of method pocketmine\\command\\Command\:\:getUsages\(\) expects pocketmine\\command\\CommandSender, pocketmine\\player\\Player\|null given\.$#'
identifier: argument.type identifier: argument.type
count: 1 count: 1
path: ../../../src/network/mcpe/NetworkSession.php path: ../../../src/network/mcpe/NetworkSession.php

View File

@@ -156,12 +156,6 @@ parameters:
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
-
message: '#^Strict comparison using \=\=\= between \*NEVER\* and 5 will always evaluate to false\.$#'
identifier: identical.alwaysFalse
count: 1
path: ../../../src/command/defaults/TeleportCommand.php
- -
message: '#^Method pocketmine\\crafting\\ShapedRecipe\:\:getIngredientMap\(\) should return list\<list\<pocketmine\\crafting\\RecipeIngredient\|null\>\> but returns array\<int\<0, max\>, non\-empty\-array\<int\<0, max\>, pocketmine\\crafting\\RecipeIngredient\|null\>\>\.$#' message: '#^Method pocketmine\\crafting\\ShapedRecipe\:\:getIngredientMap\(\) should return list\<list\<pocketmine\\crafting\\RecipeIngredient\|null\>\> but returns array\<int\<0, max\>, non\-empty\-array\<int\<0, max\>, pocketmine\\crafting\\RecipeIngredient\|null\>\>\.$#'
identifier: return.type identifier: return.type