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
Add new high-level command API #1587
Conversation
For my first discussion point: ParametersWhat are known as
|
Big undertaking, I'll be keeping a close eye on this. To speak on the whole API impl that is happening currently, it was never meant for the API to hold any real implementation and this was always viewed as a mistake. As time keeps marching on, more and more impl in the API will be moved out and into SpongeCommon. As for having a shaded lib for the command impl, while that is a nice perk I don't see it being a priority and would honestly leave it up to community members to step up and do so as it doesn't benefit our priority which is SpongeCommon. |
I believe the majority, if not all, of the implemention should be within SpongeCommon as it gives the flexibility to implemention writers to build a system that meets their exact needs and that has been the direction of everything overall in Sponge as that was its goal. Due to Sponge's code being licensed under the MIT license, it's trivial for them to utilize Sponge's implemention code if they so desire. I don't really see the merit in a library either, due to them already being able to include the code they need, and there currently only being a few active implemention of the API. |
The purist in me says that it should be this way, which is the direction I'm going in now, and I'm quite happy not to change course on that! |
b943b22
to
e52264d
Compare
I would actually take the view that it is rather handy for the API to contain some implementation. Having gone about implementing CanaryLib - I was always very thankful that large parts of it were already implemented, and required myself very little effort to get working. |
Then you would consult the most complete implementation and coordinate parts of it being separated into libraries. The goal of the API is provide the specification and not dictate how it is implemented. It says "This is what it should be" not "Here is step 1, 2, 3...etc to implement it." |
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.
I like the changes, although I do think some classes have been renamed for the sake of renaming.
/** | ||
* Represents the result of a command in Sponge. | ||
*/ | ||
public interface Result { |
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.
I'm not too keen on renaming this - this could well be used as a local variable with other classes called Result - it's just too generic a name.
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.
No point to rename this interface at all.
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.
Yeah this should remain CommandResult, especially since other results may be introduced, such as QuestionResult, as this is not abstract enough to apply to both.
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.
I wouldn't have renamed it if it was an interface. It isn't, and right now, I'm trying to avoid breaking changes. I'm not keen on the name either, but it's going to have to be something different.
* <p>Implementations are not required to implement a sane | ||
* {@link Object#equals(Object)} but really should.</p> | ||
*/ | ||
public interface 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.
Similarly to CommandResult -> Result, I do not agree with this change. This will also become a pain if-and-when #1201 gets merged as it has an annotation named 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.
Agreed.
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.
Again, not happy with the name, this time, it's me avoiding a breaking change. It is something I want to change. Give me a better name for this, and I'll change it, but I'm not prepared to break every plugin out there for the sake of keeping the name the same.
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.
It is now named the way it was supposed to be named to begin with. AnnotatedCommands came after, not before and do NOT dictate what the core interface stands for.
No different than having Entity or World.
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.
Nobody is saying that the annotated commands pr should dictate other prs - but they should be considered by others.
Regardless, the change has been made. I prefer CallableCommand
, but whatever - I expect that people will complain if and when annotated commands are merged in.
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.
Except that adding XXXX on to the name literally serves no purpose solely to make the life easier of ANOTHER PR.
CallableCommand is pointless...what does Callable add? A Command is a Command is a Command and should have a name that expresses exactly what it is, no more or no less
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.
Now for the record, the point about Result is fair. That should be named CommandResult as Result is a broad word (not like Command).
* A high level interface for {@link Command}s that handles argument | ||
* parsing and subcommand handling. | ||
*/ | ||
public interface CommandSpecification extends 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.
Fair enough changes, in line with the policy of implementation in implementation - but, I prefer the CommandSpec naming - it feels more fluent than CommandSpecification.
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.
This I'm quite happy to change back, it's in a different package so shouldn't cause too much bother.
I think you need to drop the system of backwards compatibility with this pr - let this go into a new major version, and make all the changes that should be made. |
I'm honestly happy to do that and take up the previous names of classes, but my worry about doing so is that we give developers little chance to change between versions - though the solution to that would be to merge this at the start of a new major API development cycle. I'm quite happy for the discussion to happen here about that, and I'll be seeking opinions from the Sponge leads about how much we break backwards compat closer to the time, should this all work well. Right now, I'm more concerned about bolting together my logic and making sure what I'm proposing will work, I can always rename and remove classes later. |
I'd much rather this just break compatibility. |
I think maintaining compatibility limits what you can accomplish and makes this more challenging to implement. Sponge has always been willing to make beneficial changes even if they're breaking, and I think this is such a large change that maintaining compatibility will probably just see people waiting until the last minute. I would be fine with the old solution being completely gone in order to make a new, great system that plugin developers will be happy to switch to, even if it means maintaining two separate versions for a little while. |
Does this handle Selectors better then the current system? |
cff15da
to
4920b64
Compare
* <p>Implementations are not required to implement a sane | ||
* {@link Object#equals(Object)} but really should.</p> | ||
*/ | ||
public interface CommandLowLevel { |
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.
This is an ugly name.
AbstractCommand
? (kind of weird since this is an interface and not a class, but it "works")CallableCommand
? (flip!)
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.
Absolutely nothing wrong with CommandCallable
. This idea of retaining backwards compatibility is doing this pr far more harm than good IMO.
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.
I agree, but @dualspiral doesn't seem to agree from the looks of it.
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.
-_-
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.
This idea of retaining backwards compatibility is doing this pr far more harm than good IMO.
I'm for keeping backwards compatibility so long as in the implementation there is an example provided of a plugin using both systems, or a transition example from the old system to the new. There's enough breaking changes slated for API that I don't feel it necessary to completely break the command system wholeheartedly when we can avoid more breaking changes between versions (look at deprecations versus flat out breakages).
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.
I'll keep that in mind. I also plan to write extensive documentation for SpongeDocs, based on the concepts I write in comments here, as well as a migration guide.
My fear is that pulling this and breaking everything is going to put off developers, giving an API version or two buffer will give some grace and allow developers and users to transition in a more comfortable timeline. Breaking almost all plugins with a single swipe without warning is not good for the ecosystem, no matter how good it might be for us as Sponge developers, I do not think it will be good for the community or those plugin developers that do not have a lot of time to spend on their plugins.
If it is decided to break things outright, then that will be easy to do at any point. Having to put backwards compat back in is much harder - this is my actual reason for keeping backwards compat for now, if it is decided that it should stay, rather than go. It makes naming things harder, yes, and it is a challenge to make sure the old system mostly conforms to the new - but I'd rather solve that challenge now than be told I have to face that challenge much later on.
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.
...back on the topic of this comment: what do you think about the suggested names above, @dualspiral?
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.
CallableCommand
seems resonable to me, never thought about just flipping the name around. I'll change it on next commit, but I'm also open to any other suggestions. Names are easy to change (thankfully).
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.
I'm for keeping backwards compatibility
Nobody is debating that backwards compatibility is a good thing, that will aid developers in a change that will likely affect most plugins - but - in doing so, this pr reverses good names, that haven't changes since Sponge started: because they are good.
without warning is not good for the ecosystem
Nobody is saying to do anything without warning. Sponge has always been very open about upcoming changes, and I see no reason why that would end now. (See the many PSAs on the Sponge forums)
@ryantheleach As it stands, no. What specifically needs to be resolved with that? Is there an issue? |
I'm not saying it should be necessarily addressed, the cure might be worse then the disease. But as it stands with Sponge offered commands and selectors, is that unless the command supports
then selectors don't end up working. Ideally, selectors that pass more then one entity, to a command element that only takes one entity, would cause the command to be run for each entity. Not sure what should happen for elements that take 2 arguments that take 1 entity each however.. |
In theory, selectors should still work, if only one entity can be processed. Will have to make sure that happens.
Right now, I have https://github.com/SpongePowered/SpongeAPI/pull/1587/files#diff-607ad657484bde707fee0943937768a1R44 exposed in the API, because I think that it's useful for other plugins to be able to extend it. HOWEVER, what I think might be better is if we provide a modifier that attempts to parse a selector if it detects on. That way, we could add a
Maybe by making selector support explicit, maybe it could give an indicator to the developer that more than one entity might be returned. This points to a more general problem though, in that many plugin developers don't realise that many standard parameters can return more than one object for one entry - the user and player parameters come to mind. That's what
I see the logic in what you're saying, but I think that's going to confuse things - some of my selector accepting commands do take account of multiple entities and this would break that. I think that's better left out. |
Seems like if this succeeds, then #1605 will become useless. But the things there would definitely benefit from being included. |
Also, something with tab-completion: List<String> validCompletions = ...;
if (args.hasNext()) {
String next = args.next();
validCompletions = validCompletions.stream().filter(str -> str.startsWith(next)).collect(Collectors.toList());
} This gets sort of annoying. Maybe have |
* Indicates that the parameter is optional, but will throw an exception | ||
* if an argument exists to be parsed. | ||
*/ | ||
public final ValueParameterModifier optional() { |
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.
This and optionalWeak should probably have overloads that automatically include defaultValue/Supplier.
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.
Yeah, considering you're only going to use default values with optional arguments, actually, no point keeping them separate.
* @param defaultValueSupplier The default value {@link Supplier}. | ||
* @return The {@link ValueParameterModifier} | ||
*/ | ||
public static ValueParameterModifier defaultValueSupplier(Supplier<Object> defaultValueSupplier) { |
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.
It would be awesome if this had an overload that accepted Function<CommandSource, Object>
. Example use-case would be custom versions of fooOrSource()
.
Thought that came to me through private conversation, like a |
It would basically be sugar for an |
86dc477
to
a86a1e0
Compare
Beware, long comment. This is designed to talk about the current state of play, and I am possibly going to post this to the forums soon to try to get maximum visibility and feedback from the community, including those that don't use Github. Introducing: The new Commands API - A PrimerThis explanation is designed for developers who use commands in their plugins - which is probably all of you. For Sponge API 8, we're undertaking one of the biggest refactors to the API - overhauling commands. The reasoning behind it can be found in the associated PR, so we'll concentrate on how a command will work after the API changes. Cause and ContextSponge as an API and a system is now very much centred around the tracking of causes. Events have a cause, and now commands do too. The source of a command will now ultimately be the This fits in well with Minecraft 1.13, as the cause of a command is no longer directly a As Zidane puts it:
This is one of the discussions around the API, do we do away with the At the basic level (as it is at the moment), the process method on a command is now: CommandResult process(Cause cause, String arguments) throws CommandException; There are two directions we can go with this to define the target of this. Either:
My current implementation (which is this way to test things and is in no way complete) is to add three keys to the context that can (but don't have to be) used (there are four, but the fourth is likely to be removed):
My thought is that with the combination of the There are more discussions to be had around the form of the command methods and how they should work. They will be discussed in later sections. Starting at a low level - the
|
a86a1e0
to
2533abc
Compare
So I touched on this in the livestream a bit, but I'll go into more detail here. So, I think a lot of us that use the current command system is guilty of is not using onlyOne in the places it should be used. I definitively know I am. The most common case of that is taking a player in a command. Usually, I just need a single player, but I forget to use onlyOne. For normal stuff, it works fine, but if someone tried to use a selector for example, then I have no idea what would happen. I'd love to see some sort safeguard or maybe even a simple warning that is printed when you only get a single value for some value that can have multiple values (it probably won't always have multiple values, but would still be nice for the safeguard to kick in |
IMO a goal of any command API should be that you could rewrite all the vanilla commands in it. So the ability to use Brigadier's new NBT stuff should definitely be in it. |
bef9c8d
to
d7957db
Compare
I've had a quick look at this. Do you think you could encode into the generics of Parameters whether they return 1, return many, or return maybe 1? I predict someone's next question would be, how would you get that information out of the context? well if we reused the parameters as the command keys rather then texts, it would give you the typing information needed. e.g.
Or am I crazy? I just want to get rid of this pattern. context.getArgument(Text.of("player")).get()//throws exception that makes sense to a java programmer, not a user. gets wrapped by commandexception somewhere, and presented to end user as untranslateable error message. |
In theory, anything is possible. In practice, not really. All you are doing is moving the problem around. You are moving the generic check from the executor to the parameter and changing the key to be the parameter itself. Problems that might occur are:
There are two ways around this, of which, I've gone for the second:
T getOneUnchecked(String key) {
try {
T ret = (T) map.get(String key);
if (ret == null) {
throw new CommandException(...);
}
} catch (ClassCastException ex) {
throw new CommandException(...);
}
} If the returned object is an optional (so using I admit that none of this is ideal at all, but using the parameter as a key is going to complicate things for some developers I think - best we protect from some mistakes as gracefully as possible rather than adding a barrier that budding developers might not understand. However, we should also impress upon the developers that they need to test their code. We cannot protect against everything, because if we do, the system will become unusable. |
In my mental model I didn't consider this as a possibility. The way I've imagined commands is one parameter is associated with one key, with one type, it's just a matter of whether that type is a union type or some other wrapper. I can understand wanting duplicate parameters, but they are more like parameter archetypes, they still need to be built with a key anyway, which then uniquify'd them in my head. "re usually specified inline in the command(spec) builder" traditionally yes, but you either needed to supply a key (which in my plugins I've always used a field constant to reduce errors) so I would have been using a field constant for the parameter instead.
You wouldn't be returning an optional, unless it was an optional parameter, in which case it's on the user.
This is the path that I would have taken, but my coding style is probably different, I was confusing the ideas of ParameterKeys and Parameters I think.
This works, and would be better then the current situation, without changing what people are used to too much.
Sure. I just thought this was one area that we could improve upon, but it looks like I hadn't considered all angles. |
The current command system is all API implemented, and uses final classes. It is very difficult to change without breaking, so instead, this is an entirely new system built to be Common implemented. Currently, most of the work has focused on parameters. Instead of wrapping parameters/arguments, we now modularise the code and use a builder to put it all together, a bit like CommandSpec. A specification will go up on the PR once I get around to putting one up. * Make commands accept a cause * Add parameters from #1605 * Add colour parameter
d7957db
to
17b8876
Compare
What's the status of this? |
I think we should consider re-opening this PR to have a fresh list of comments and clearer description with the updated status. This PR is becoming too much for GitHub to load sometimes. |
The PR has mostly stalled due to my current work and life workload (reasons for which I'll restrict to private circles), as well as the stable-7 cleanup, but it does work (mostly) and probably doesn't need much more on it as a first pass, though I do have a couple of things I want to do However, I'm also considering breaking this into two:
There is a lot in this PR, more than I anticipated, and it might allow things to go more quickly if it's broken up as such. As this targets bleeding, I also don't mind getting stuff in that works, then tweaking it after, such as where to include the I'll try to spend a bit of time on it later this week when I have a bit more time, but it may be a couple of weeks before I can really get onto this with anger. |
This PR looked so promising and yet it have stalled, any updates? |
Closed in favour of #1958 |
SpongeAPI | SpongeCommon | SpongeForge
This is a new API and implementation of a high-level command system to bring the design inline with Sponge's general design and to try to resolve some of the perceived weakness of the system. It was one of the things I said I'd look into when I joined the Sponge team... so here we go!
Motivation & Goals
The command system is out of place in the current ecosystem and parts of it can be difficult to use. It doesn't have much in the way of abstraction, and this is hindering potential development of it. I've also seen quite a few plugins eschew the argument system, preferring to just split the string and doing the parsing themselves. Consider the following points:
So, as a result, I've been playing about with creating a new high level system to try to make it easier to resolve, if not resolve, some of the issues that currently plague the system.
It's not finished, and it certainly doesn't work right now, but with community help, we can flesh out a system that is hopefully more flexible and more usable than before.
It's also possible that this ends up being a very bad idea and/or more complex than before and thus doesn't get merged. A major goal is to make the command system, particularly parameters/arguments a little easier to work with, if we can't do that, this PR will become worthless.
Another goal is to modularise the system enough so that we can build other systems around it with ease, for example, some annotation based system could make use of the value parameters independently, and modifiers can be specified independently, without worrying about how the wrapping might otherwise work. However, that's further down the road.
Consideration: do we want some of this to be put into Common?
Third party implementations, on the other hand, may benefit from API implementation - this would make the command system consistent amongst different platforms, and as it doesn't need to rely on Minecraft, it doesn't matter where it goes. However, this means another library to consider.A halfway house would be to make the implementation part of the command system an external library shaded in, and only otherwise depends on the API - third party implementations can then depend on our solution, whilst keeping the API clean for developers to work against.This is something that needs some careful consideration, but right now, I'm going to keep things split API/Common, because I still think that having the API interfaces and the implementation separate as much as possible is a plus, and this way, it forces me to think about how to keep things separate. It shouldn't be too difficult to move things around if necessary.Answer: yes. This is being split into API and Common. There will be a couple of minor useful classes for developers to use in the API, but the bulk of the implementation will be moved into Common.
Heads up! This will break almost every plugin out there.
It has been decided that we’re just going to break things. Plugins that use commands will break when this is pulled, no way around that. However, as discussion has shown: it’s the preferred chioce and it makes things much easier on us.
This is where the developer community comes in!
This PR is in very early stages, as such, I'm looking for feedback and suggestions on the direction of travel of the API. Things will change, ideas will come and go. It doesn't quite need a review yet, but feedback on the ideas are more than welcome. I will try to write up specifications of what I'm doing as I go in the comments, ready for (constructive) feedback and ideas.