-
-
Couldn't load subscription status.
- Fork 1.6k
Firebombing of commands for the first time in 10 years #6837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: major-next
Are you sure you want to change the base?
Conversation
|
Is it safe to use non-mb_* prefixed methods while parsing? Haven't looked code deeply but I saw a lot of substr and strlen used in code - wouldn't this affect to non-english commands? |
|
It should be fine. We only care about byte lengths, and splittable whitespace chars will never be more than 1 byte. |
this was a problem with cmdalias, where just writing cmdalias alone would invoke cmdalias list. In itself this wasn't particularly harmful, but it could've been a problem for other commands like timings.
Since it only compares the exact literal bytes, it's possible to have trailing characters that don't match. For example, /timings ons would crash because the 'on' overload would match, but leave a trailing 's' behind.
we only care if it's in the exact position defined by $offset.
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 <level>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.
|
@inxomnyaa raised concerns about performance when processing subcommands. I'm not sure to what extent this is a problem, but I have seen some plugins create much more complicated command signatures than vanilla. The way that overloading works, "subcommands" can be implemented as overloads using literals in this system, but it currently has to try each one, one at a time, to find a matching overload. This could become a problem if many overloads are present. This is sometimes the case with complex plugin commands, where nested commands blow up to create a large number of overload permutations. Possibly, it might be advantageous to detect which overloads have the same first N parameters, so that if other overloads have the same leading arguments, we can eliminate multiple overloads at once in some cases without having to reparse the entire command over and over again. It might also be worth exploring whether we can avoid bruteforce lookups when some or all overloads have a literal as their first parameter(s). This would be a common case for subcommands. |
| use function trim; | ||
| use const PHP_INT_MAX; | ||
|
|
||
| abstract class Command{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should Command be non-abstract now? There are no abstract functions, and there doesn't seem to be a good reason why we need to force users to create a separate class for each command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Maybe this also makes ClosureCommand obsolete too.
|
Optimizing overload selection shouldn't be a blocker for this PR. Plugins that are particularly concerned about huge number of overloads could always fall back to RawParameter to leverage their own optimized parsing algorithm. |
Yeah, but then they'd lose client-side command hints, which we all know is the thing plugin devs really care about, for some reason... |
|
any plan to add Adds a PlayerArgument resolves actual Player instances, not raw name strings. This Implementation:
|
|
I don't know about the tick stuff, that doesn't seem necessary to me, but yes there are plans to expand the system. I'm currently unsure how to deal with target args because of the Enums, soft (dynamic) enums, and composite types like Vector3 are also not supported yet. This PR implements the minimum of what was needed to get PM's builtin commands moved over to the new system. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wanted to leave some of my thoughts on here :)
|
|
||
| $overloads = []; | ||
| foreach($command->getOverloads() as $overload){ | ||
| //TODO: we should only send permissible overloads here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason this was left as todo? Does it cause problems with the client or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never got around to it
| //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. | ||
| //The cause for this seems to be undefined behaviour on the client linked to the order and | ||
| //number of certain hardcoded enums like GameMode, IntParam etc which we don't provided. I |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick, *provide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Over all, complex, but i can't say i hate it. Good job
This PR implements a command overloading system. Partially inspired by @alvin0319's prior (abandoned) work on #3871, but with deeper integration.
Relevant issues
Highlights
/tpcommandif(isset($args[$offset]))- your callback simply won't be called if the number of arguments is wrong or the arguments can't be parsed the way you asked for/me,/say,/tellnow handle quoted inputs correctly - previously/say hello "some user"would broadcasthello some user, but now broadcasts exactly what you typed, i.e.hello "some user"How it works
int $paramstringparameter, you can do?string $param = nulland check fornullto see if the parameter was specified or not.Itemobject; seeMappedParameter, and how it's used in commands like/enchant,/effectParameterobjects - these aren't given to the callback, but must be typed exactly in the position specified (e.g. a subcommand name); see how these are used for/effect <target> clear,/time set <timestamp>etcIntRangeParameter("xp")whose value is accepted byint $xp,?int $xp,int|string $xp, but notstring$xp)Command->executeOverloaded()is calledGotchas
Follow-up
x y z(which are actually 3xRelativeFloatparameters)pocketmine/locale-dataParameterinfos from a provided callback instead of having them explicitly specified#[Bounds(0, 100)] int $paramcould be explored, but this would only work for simple stuff known ahead of timeplugin.ymlcommands, which are hella dated, awful, and can't take advantage of this cool new system (Scrapplugin.ymlcommands #6506)API changes
Command->__construct()now requires a non-empty list inarray $overloadsCommand->executeOverloaded()- called bySimpleCommandMapCommand->getOverloads()Command->getUsages()- returns usages for all overloads the givenCommandSenderhas permissions forCommand::fetchPermittedPlayerTarget()- similar toVanillaCommand->fetchPermittedPlayerTarget(), but now accessible to all commands instead of justVanillaCommandinheritorsCommandStringHelper::parseQuoteAwareSingle()- parses a"quoted string \"like this\""intoquoted string "like this"- similar toparseQuoteAware()but only parses 1 arg instead of the entire commandCommand->getPermissions()- permissions are now defined per-overload instead of for the whole commandCommand->setPermissions()Command->testPermission()Command->testPermissionSilent()Command->getUsage()- command usages are now derived from overloads, and there may be more than one per commandCommand->setUsage()VanillaCommand- no longer served any purpose since it was all about manual arg parsingLegacyCommand- provides a similar API to the old-styleCommandfor a quick & dirty upgrade path for old code if you don't want to switch to the new system immediately (this is also still used byClosureCommand,FormattedCommandAliasandPluginCommandfor the time being)LegacyCommandwill use your old-style hardcoded usage message if provided, but you should consider switching to the new system. This is only provided to ease upgrading and won't stick around forever.Parameterand various subclasses inpocketmine\command\overloadpackageCommandOverload- accepts parameter info and callbacks to be given toCommand->__construct()ParameterParseException- thrown when aParametercan't parse a command tokenUtils::validateCallableSignature()now acceptsClosure|Prototypefor both arguments