-
Notifications
You must be signed in to change notification settings - Fork 273
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
Implement block states for commands and /testforblock command #579
Conversation
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 you go, that's my feedback.
|
||
@Override | ||
public boolean matches(BlockStateData state, Wool data) throws InvalidBlockStateException { | ||
if (state.containsKey("color")) { |
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 checking whether the BlockStateData contains a key actually leads to the wrong results. This won't catch the case where BlockStateData contains an invalid property for this material. In this case, I would recommend copying all the keys into a new ArrayList, and then while looping over the iterator. This way, whenever a key is successfully matched, you can call iterator.remove() to remove that key from the array. The matches() method then can only return true when the keys list is empty, ensuring that this case is caught.
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.
Doesn't getValidStates()
solve this?
|
||
public abstract T read(Material material, BlockStateData data) throws InvalidBlockStateException; | ||
|
||
public abstract boolean matches(BlockStateData state, T data) throws InvalidBlockStateException; |
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 feel like sticking this on the reader is a bit disjoint with how I implemented the Entity's tag matching. I'm not opposed to sticking it on the reader, but and if you want to keep it like this you should think about changing how the CompoundTag's matching works. Otherwise, I'd say you should implement the match method on the BlockStateData, similar to how CompoundTag implements its match method.
|
||
import java.util.HashMap; | ||
|
||
public class BlockStateData extends HashMap<String, String> { |
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.
Extending an implementation of Map<> seems like the wrong way to go here. In my experience, this makes it harder to refactor, especially if another implementation of map is needed, and can dilute the reason for having BlockStateData vs a regular map. It's convenient, yes, but often introduces a design smell. I usually prefer introducing the map as a field and exposing only the relevant delegate methods, making it clear what is going 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.
Good points, I will change this.
throw new InvalidBlockStateException(material, state); | ||
} | ||
BlockStateData data = new BlockStateData(); | ||
String[] split = state.trim().split(","); |
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 a case where I'd say a small Regex could make the expected syntax a bit clearer, and reduce the number of cases you need to check to throw InvalidBlockStateException. Looks like the regex you want is "^\\s*((\\w+)\\s*=\\s*(\\w+))\\s*(,\\s*(\\w+)\\s*=\\s*(\\w+)\\s*)*$"
. Looks intimidating, yes, but that is just keeping compatibility with the trimmed code above.
To use this regex, you'd be iterating through all the groups (starting at index 1 because the group at index 0 always matches the entire string), but taking only the second and third group out of every three, as the first will represent the entire "key=value" match.
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 hadn't thought of using a regex here, I will consider it. My only concern would be readability, but that can be resolved with a comment.
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.
After checking, the regular expression you provided doesn't work all the time. For the string a=b,c=d,e=f
:
Full match | a=b,c=d,e=f |
---|---|
Group 1 | a=b |
Group 2 | a |
Group 3 | b |
Group 4 | ,e=f |
Group 5 | e |
Group 6 | f |
This would skip the middle section.
return false; | ||
} | ||
String itemName = args[3]; | ||
if (!itemName.startsWith("minecraft:")) { |
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 as familiar with the plugin side of things, but if a plugin introduces a new block, will it still start with "minecraft:"? If not, then this check would seem to fail when trying to test for a "foo-plugin:bar" block.
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.
You can't register new blocks/items with Bukkit. The ids are registered in the ItemIds class and all start with the minecraft namespace. Adding this functionality is outside of the scope of the PR.
return false; | ||
} | ||
if (args.length > 4) { | ||
String state = args[4]; |
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.
Since we know this same "check the data value or state argument" is used in at least three commands (clone and testforblock, and testforblocks), mind abstracting this check out into a CommandUtils method? May want to return a custom type with two properties, Integer data
and BlockStateData stateData
, for ease of use in these commands.
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.
Will do.
} | ||
} | ||
if (args.length > 5 && block.getBlockEntity() != null) { | ||
String dataTag = String.join(" ", new ArrayList<>(Arrays.asList(args)).subList(5, args.length)); |
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.
Given we do this CompoundTag check in a few commands as well now, we might want to think about also abstracting this out into a CommandUtils method.
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 point, but it's outside of the scope of the PR. I'll consider it once this is merged.
* Fix exception when parsing a state with no value (something=) * Move some common state utils to CommandUtils * Boxed map instead of extending it (BlockStateData)
I need approval to merge this for the release of 2017.10 on Wednesday. I can address the documentation and further design changes later on. |
Merged in 18a3b49. |
This PR adds the system to parse and compare block state data as strings instead of numerical data values. This feature was added to some commands in 1.11, and allows using
color=orange
instead of the data value1
for wool blocks, for example./testforblock
command, which uses this new functionality./testforblock ~ ~-1 ~ minecraft:wool color=light_blue
~ ~-1 ~
), where the relative Y value would be shifted by 0.5 blocks like the X and Z axis. Some tests needed to be fixed to pass with this correction.net.glowstone.block.entity.state
package to avoid confusion.I still need to add some documentation for the block state parsing system.
Related to #499