From 4a3f528c4bc807206ccef36c252c7808517373eb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 12 Oct 2025 18:49:37 +0100 Subject: [PATCH] Add basic client-side hint support this doesn't support enum or player name auto complete for now since the API has no way to expose that information. however, we can get some of the benefits with basic types xp L is not supported because the stupid fucking client crashes when it's used and I wasted all day trying to debug it. Someone else can fix that if they care. --- src/command/Command.php | 6 +++ src/command/overload/CommandOverload.php | 10 +++++ src/network/mcpe/NetworkSession.php | 55 +++++++++++++++++++++--- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/command/Command.php b/src/command/Command.php index ba54ab4cd..1c35b567c 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -108,6 +108,12 @@ abstract class Command{ return false; } + /** + * @return CommandOverload[] + * @phpstan-return list + */ + public function getOverloads() : array{ return $this->overloads; } + /** * @return Translatable[] * @phpstan-return list diff --git a/src/command/overload/CommandOverload.php b/src/command/overload/CommandOverload.php index 694b6a3b1..c0064e90b 100644 --- a/src/command/overload/CommandOverload.php +++ b/src/command/overload/CommandOverload.php @@ -129,6 +129,16 @@ final class CommandOverload{ return $result; } + /** + * @return Parameter[] + * @phpstan-return list + */ + public function getParameters() : array{ return $this->parameters; } + + public function getRequiredParameterCount() : int{ + return $this->requiredInputCount; + } + /** * @return string[] * @phpstan-return list diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 447a1798d..9aae17543 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -26,6 +26,10 @@ namespace pocketmine\network\mcpe; use pmmp\encoding\ByteBufferReader; use pmmp\encoding\ByteBufferWriter; use pmmp\encoding\DataDecodeException; +use pocketmine\command\overload\FloatRangeParameter; +use pocketmine\command\overload\IntRangeParameter; +use pocketmine\command\overload\RawParameter; +use pocketmine\command\overload\RelativeFloatParameter; use pocketmine\entity\effect\EffectInstance; use pocketmine\event\player\PlayerDuplicateLoginEvent; use pocketmine\event\player\PlayerResourcePackOfferEvent; @@ -90,6 +94,7 @@ use pocketmine\network\mcpe\protocol\types\command\CommandData; use pocketmine\network\mcpe\protocol\types\command\CommandEnum; use pocketmine\network\mcpe\protocol\types\command\CommandOverload; use pocketmine\network\mcpe\protocol\types\command\CommandParameter; +use pocketmine\network\mcpe\protocol\types\command\CommandParameterTypes; use pocketmine\network\mcpe\protocol\types\command\CommandPermissions; use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm; use pocketmine\network\mcpe\protocol\types\DimensionIds; @@ -1098,6 +1103,9 @@ class NetworkSession{ $commandData = []; $globalAliasMap = $this->server->getCommandMap()->getAliasMap(); $userAliasMap = $this->player->getCommandAliasMap(); + $language = $this->player->getLanguage(); + + $literals = []; foreach($this->server->getCommandMap()->getUniqueCommands() as $command){ if(count($command->getUsages($this->player, "")) === 0){ //no permitted overloads @@ -1114,18 +1122,55 @@ class NetworkSession{ //use filtered aliases for command name discovery - this allows /help to still be shown as /pocketmine:help //on the client without conflicting with the client's built-in /help command $lname = strtolower($firstNetworkAlias); - $aliasObj = new CommandEnum(ucfirst($firstNetworkAlias) . "Aliases", $aliases); + $aliasObj = $command->getId() === "pocketmine:xp" ? new CommandEnum(ucfirst($firstNetworkAlias) . "Aliases", $aliases) : null; + + $overloads = []; + foreach($command->getOverloads() as $overload){ + //TODO: we should only send permissible overloads here + $parameters = []; + $required = $overload->getRequiredParameterCount(); + foreach($overload->getParameters() as $k => $parameter){ + if(is_string($parameter)){ + $literalEnum = $literals[$parameter] ??= new CommandEnum("Literal_$parameter", [$parameter], isSoft: false); + $parameters[] = CommandParameter::enum( + $parameter, + $literalEnum, + flags: CommandParameter::FLAG_FORCE_COLLAPSE_ENUM, + optional: $k >= $required + ); + }else{ + $simpleArgType = match(true){ + $parameter instanceof FloatRangeParameter => CommandParameterTypes::VAL, + $parameter instanceof IntRangeParameter => CommandParameterTypes::INT, + $parameter instanceof RawParameter => CommandParameterTypes::RAWTEXT, + $parameter instanceof RelativeFloatParameter => CommandParameterTypes::RVAL, + default => CommandParameterTypes::ID //string + }; + $suffix = $parameter->getSuffix(); + $name = $parameter->getPrintableName(); + $translated = $name instanceof Translatable ? $language->translate($name) : $name; + if($suffix !== ""){ + //umm... client only allows suffixes on integer params??? + //TODO: as of 1.21.111, the client crashes if we try to provide actual suffixes for /xp and + //I can't be bothered to debug it. Fill this with string for now. + //$parameters[] = CommandParameter::postfixed($translated, strtolower($suffix), flags: 0, optional: $k >= $required); + $parameters[] = CommandParameter::standard($translated, CommandParameterTypes::ID, optional: $k >= $required); + }else{ + $parameters[] = CommandParameter::standard($translated, $simpleArgType, optional: $k >= $required); + } + } + } + $overloads[] = new CommandOverload(chaining: false, parameters: $parameters); + } $description = $command->getDescription(); $data = new CommandData( $lname, //TODO: commands containing uppercase letters in the name crash 1.9.0 client - $description instanceof Translatable ? $this->player->getLanguage()->translate($description) : $description, + $description instanceof Translatable ? $language->translate($description) : $description, 0, 0, $aliasObj, - [ - new CommandOverload(chaining: false, parameters: [CommandParameter::standard("args", AvailableCommandsPacket::ARG_TYPE_RAWTEXT, 0, true)]) - ], + $overloads, chainedSubCommandData: [] );