Problem comparing block variant types

Every time I think I conquer this it stumps me again. I’m porting my block protection / economy plugin from Bukkit to Sponge. The protections and individual block prices are all setup in the plugin config file. I need to be able to identify block variants not only for permission error messages (“You can’t break dark prismarine on this plot!”), but also to allow the user to specify different prices for block variants. An example config file section:

"minecraft:prismarine":
  type: "prismarine:rough"
    name: prismarine
    price: 0.75
  type: "prismarine:bricks"
    name: prismarine brick
    price: 1.00
  type: "prismarine:dark"
    name: dark prismarine
    price: 1.50

So for each block id, minecraft:prismarine in this case, I make a BlockPermissionSet. If the id has variants, like prismarine does, I create a BlockPermissionSet for each one, but the variant BlockPermissionSets get stored in an internal arraylist named subtypes. If the block has no variants, like cobblestone, that list is empty. With Bukkit, I used the block data value to identify block variants, so instead of type: “prismarine:dark” i had data: 2. Not elegant, but it worked and it was the only way to do it with Bukkit.

So now we have CatalogTypes to identify the blocks. I made a map of block IDs to CatalogType classes…

typeMap.put("minecraft:carpet", DyeColor.class);
typeMap.put("minecraft:cobblestone_wall", WallType.class);
typeMap.put("minecraft:dirt", DirtType.class);
typeMap.put("minecraft:double_plant", DoublePlantType.class);
typeMap.put("minecraft:double_stone_slab", SlabType.class);
typeMap.put("minecraft:double_stone_slab2", SlabType.class);
...

and a helper method…

public Class<? extends CatalogType> getVariantClass(String id) { return typeMap.get(id); }

So I could get the correct variant types from the config file like this:

private final String id;  // The block ID for this permission set
private String variantID; // The variant ID for the block variant
private BlockType blockType; // The BlockType for the ID from the config file
private CatalogType variantType; // The CatalogType for the block variant
private List<BlockPermissionSet> subtypes; // All the other variant types for this block ID

public BlockPermissionSet(ConfigurationNode node) {
    id = (load ID string from config file);
    variantID = (load variant ID from config file, if applicable);
    blockType = Sponge.getRegistry().getType(BlockType.class, id);
    variantType = Sponge.getRegistry().getType(typeMap.get(id), variantID);
}

So now variantType is stored in the block permission set. I store it as a CatalogType because we don’t know what type it will be at compile time. When a block break event occurs, this permission set will be retrieved by another class that has a HashMap of IDs and block permission sets, and then passed to the event handler. Since the event handler doesn’t know if this block has a variant or not, the event handler calls this method in BlockPermissionSet:

public BlockPermissionSet getByBlock(BlockSnapshot block) {
    if(variantType == null) return this;
    if(variantType == [the variant type of the block]) return this;
    for(BlockPermissionSet bps : subtypes)
        if(bps.getVariantType() == [the variant type of the block])
            return bps;
}

So if this permission set has no variant, the method returns itself. If there are variants, it checks itself for equality and returns itself if there is a match. If not, it checks each subtype for equality and returns that BlockPermissionSet if there’s a match.

The problem is the part in brackets. I have the variant type I want to compare to, for example. PrismarineType.DARK. How do I compare this to the type in a BlockSnapshot? I know it’s retrieved usings Keys, like this:

CatalogType variantType = (our stored variant type, for examlple, PrismarineType.DARK)
BlockSnapshot block = (some block);
Key<? extends BaseValue<?>> key = block.get(Keys.PRISMARINE_TYPE); // I know this refers to a specific type, but I could make another map of block IDs and keys and retrieve it that way
[how do I compare my CatalogType to the one contained in this key?]

The problem of course, is that I can’t refer to the type of the key directly, since there are many different types of block variants (PrismarineType, StoneType, BrickType, PlantType, etc.). It would be easy if I could just say

Key<Value<PrismarineType>>

instead of Key<? extends BaseValue<?>>, but at compile time there’s no way to know which type the set will contain. I would include the entire BlockPermissionSet class, but there’s a lot of crap not related to my problem that would just be confusing. Hopefully someone with a better grasp of the data API can help me out here. Thanks in advance!

Just use BlockState instead of BlockType. Then they can enter it like minecraft:prismarine[variant=dark_prismarine] and the registry can turn this into a BlockState because they’re catalogued too.

You are my hero! Thank you so much I’ve been beating my head against the wall on this. So you’re saying instead of storing a BlockType and CatalogType to identify the block, just create and store a BlockState like this:

private BlockState blockState;

blockState = Sponge.getRegistry ().getType (BlockState.class, "minecraft:prismarine[variant=dark_prismarine]") // from the configuration file

Later...

public BlockPermissionSet getByBlock (BlockSnapshot block) {
    if block.getState ().equals(this.blockState)
        return this;
}

Will that also work if the block has other things stored in the state, like axis data for logs or Connected data for glass panes? If not, I can probably work out a solution by looking at the raw text strings.

Yes, you just have to have every used variable present IIRC (i.e. log type and axis).