-
Notifications
You must be signed in to change notification settings - Fork 400
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
Pass context to ArgumentType.parse #76
Comments
Have you considered brigadier/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java Lines 97 to 106 in ff42867
if an From looking at a decompile, Minecraft seems to get around this limitation by parsing the intention of every argument into some object (i.e. relative coordinates, the I can think of one place this model does break down though, in for example a Discord bot, you might want an argument type for a role - reasonably this should only work for roles within the guild the command was sent, and although you can easily parse a given snowflake, make sure it's the correct format, make sure it actually resolves to a role - there's no way to check that this role exists in the given guild at parse-time, the only solution would be to throw an exception at execution-time after additional parsing, so clearly this doesn't work for everyone. ProposalI propose this: A new step in the execution process, right after parsing and before execution where input is validated and finalized. For most arguments (like every single one that's built into brigadier) nothing would be done here, but for some arguments that require access to the source, the input should be parsed at parse-time and made sure it's the correct format. For my discord example, this would be making sure it's a valid snowflake, that the timestamp isn't in the future or whatever, and storing it as a Snowflake or a long. Then at validate-time additional processing can be done, which in my discord example could make sure that the snowflake corresponds to a valid role, and that the role exists in the guild, and finally present the actual command context with a Role instead of a Snowflake. This is where the argument would have access to the command source, and this step is skipped when searching for ambiguities (which is why it'd be important to actually parse the format at parse-time). This could also allow multiple arguments that use the same or similar format, but have different valid values (i.e. a user and a role both use snowflakes, however their snowflakes won't ever match) to be unambigous before execution-time. An argument can throw at validate-time the same way it does at parse-time and to the user it should look the same, and mostly everything should be nearly unchanged. Minecraft seems to follow this model actually, just that it embeds validate-time inside of execution-time and completely outside of brigadier, which seems a bit strange in my opinion, and could probably be improved by being a part of brigadier. Overall this shouldn't be a big change, it could be non-breaking if a default implementation for the validate stage were to just return the parsed value. |
I'll look into where and how this validation step will be implemented, thanks for reminding me about isInputValid. |
I was actually looking through the issues to see any unfixed issues in brigadier to fix before releasing a port of it to Dart - i think brigadier is a nice library, but personally i don't like Java that much I've implemented this proposal in my port by using a shared type
The
In In In In the parse stage, the Right before the execute stage, where the command path would already be found from the parse, every argument is validated when the Now, what if this argument doesn't throw in parse but would throw in validate? what about isInputValid that doesn't call validate? well, it should throw in the parse then if it would always fail at validate, but for any sender-specific validation that throws most of the time except maybe one sender that can't even exist, as far as the input being valid is concerned, the command is fine, as isInputValid asks "is this input a valid command?", not "is this input a valid command from this sender?". Although of course this doesn't map 1:1 to java, but it should give you an idea of how i've done it so you can implement it the best way for your language? (mainly the asynchrony, which from what i can tell is a breaking change unless you specifically implement it as a fourth AsyncArgumentType or something like that, but also that covariant parameter which seems to not really work, perhaps you'll have to settle with Object and force implementers to cast to their intermediate type, assuring them that's completely safe, or you cast that in the library itself and make implementers instead implement an overload specific to the ComplexArgumentType, and in SimpleArgumentType just return the value casted to T) I think a breaking change here would be preferable to really ugly argument type organization. (and might actually be necessary if you implement asynchrony in your implementation, seeing as the whole thing seems to be unmaintained, with the latest commit nearing 2 years ago, it wouldn't be that big a deal to implement such a change on a fork of it, seeing as nobody relies on your fork yet anyways, so no code would actually be broken by it) Also, you might find yourself wondering why i didn't mention suggestions (those could be sender specific, especially what if you want async?) when describing my argument type suggestions, and this is simply because i haven't implemented suggestions yet, which is also why i didn't release this yet when it might be seemingly complete (because it is, it can dispatch commands just fine at this point in time) |
I don't know if anyone is even going to find this useful, but here it goes I guess. I needed context when parsing to check if a string matched the effective name of any user in a discord server. This is to prevent a user from specifying a name for a user they don't share a server with. It's also needed because if you don't have the context, you can't parse by the player's nickname, you can parse by their actual name. I modified brigadier to pass in the context when parsing arguments. It seems to work for me. It is obviously not compatible with Mojang's brigadier. Fork is here if anyone is interested: https://github.com/TheGameCommunity/brigadier |
I guess I'll link my solution as well; I basically wrote my own implementation of a command parser, loosely based on Brigadier for use with a Kotlin DSL: https://github.com/Martmists-GH/command_parser |
For a few of my projects I'm considering using Brigadier, however one limitation has been stopping me from using this: ArgumentType.parse is not context-aware.
In many cases, the object to be returned requires context of where or by who the command was invoked. The simple solution would be to pass the source object to the parse method as second parameter, however this would cause breaking changes in the library.
To solve this, I propose to give the
parse
method two signatures:parse(StringReader reader)
andparse(StringReader reader, Object context)
, both with a default implementation which returns null.Sadly due to how the current arguments are implemented they do not supply the type of the context, but on a per-application basis this could be done with a manual cast in the parse() method. To handle both functions, the following could be done:
This way users can implement context-aware parse methods without breaking any existing code, unless someone implements an ArgumentType that returns a nullable value.
The text was updated successfully, but these errors were encountered: