How to get block variant?

What do you mean it’s wrong?

is STONE_TYPE just for Blocks like stone, diorite etc? Because there is a SAND_TYPE too…

Well stone isn’t sand and sand isn’t stone.

If you’re wondering why there isn’t just a variant key, it’s a semantic difference really.

However it makes it so that you always get what you expect.

This way you can’t get sand type from stone and vice versa.

Thanks!
I’m using it like this now:

public void onLogBlockToolUse(ChangeBlockEvent event) {

        Optional<Player> LoptPlayer = event.getCause().first(Player.class);
        if (LoptPlayer.isPresent()) {
            logger.info(event.getTransactions().get(0).getFinal().get(Keys.STONE_TYPE).get().getId() + "");
        }}

When I place Blocks like Stone, Diorite and other types of stone, everything works, but when I try it with blocks liek dirt / sand, I get following error:

Could not pass ChangeBlockEvent$Place$Impl to Plugin{id=CaveCore, name=CaveCore, version=1.1}
java.util.NoSuchElementException: No value present
        at java.util.Optional.get(Optional.java:135) ~[?:1.8.0_66]
        at me.Hitzk0pf.CaveCore.listeners.BlockListeners.onLogBlockToolUse(BlockListeners.java:73) ~[BlockListeners.class:?]
        at org.spongepowered.common.event.listener.ChangeBlockEventListener_BlockListeners_onLogBlockToolUse4.handle(Unknown Source) ~[?:?]
        at org.spongepowered.common.event.RegisteredListener.handle(RegisteredListener.java:92) ~[RegisteredListener.class:1.8-1577-2.1-DEV-880]
        at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:228) [SpongeModEventManager.class:1.8-1577-2.1-DEV-880]
        at org.spongepowered.mod.event.SpongeModEventManager.postBulk(SpongeModEventManager.java:216) [SpongeModEventManager.class:1.8-1577-2.1-DEV-880]
        at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:253) [SpongeModEventManager.class:1.8-1577-2.1-DEV-880]
        at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:240) [SpongeModEventManager.class:1.8-1577-2.1-DEV-880]
        at org.spongepowered.common.SpongeImpl.postEvent(SpongeImpl.java:111) [SpongeImpl.class:1.8-1577-2.1-DEV-880]
        at net.minecraft.world.World.handlePostTickCaptures(World.java:808) [aqu.class:?]
        at net.minecraft.world.WorldServer.onUpdateBlocks(WorldServer.java:129) [qt.class:?]
        at net.minecraft.world.WorldServer.func_147456_g(WorldServer.java:454) [qt.class:?]
        at net.minecraft.world.WorldServer.func_72835_b(WorldServer.java:221) [qt.class:?]
        at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:693) [MinecraftServer.class:?]
        at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:364) [po.class:?]
        at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:598) [MinecraftServer.class:?]
        at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:478) [MinecraftServer.class:?]
        at java.lang.Thread.run(Thread.java:745) [?:1.8.0_66]
1 Like

Well right. It’s supposed to do that.

Only stone has stone type data…

You need to check if it’s stone or cobblestone first before you can try to get it’s type.

Also why do you need to add the + "" at the end? It’s already a string…

I know, that this is a string, just ignore it :smile:
How should I do that? This sounds like a huge walk around?
Is there really no way to simply get the variant of every block?
I mean this fires onBlockChange, so I have to check x times which block was changed?

Well let me ask you.

Do iron blocks have variants?

does grass have variants?

does bedrock have variants?

Not all blocks have variants, and they certainly don’t all have the same variants. They need to be separated to make any sense at all.

Would it be better, when I take (e.g.) minecraft:stone[variant=diorite], check with a regex if it contains the word “variant” and use another regex to get the value of the key variant?

No.

Unless you want your code to break unexpectedly…

toString() is generally not a reliable source of data…

Hmm, so I have to use for example 20 “ifs”? Really? :frowning:

What do you even need the data for anyway?

This is looking like an XY problem.

I’m building a job plugin. A user gets different amounts for different blocks. I don’t want to let the users get the same amount of money for breaking / placing (e.g.) stone and diorite…

Yeah it looks like you get to have fun with a bunch of if statements.

The problem is that variant returns funny things for things like slabs and stairs.

You pretty much have to do this case by case…

1 Like

Okey…
Thanks for helping me!

As an alternative to using the Data API, you can use BlockTraits. These operate by using strings, so for example just use “variant” to get the variant trait of a block.
Note than in minecraft not all variants are called “variant” for example wool color is called “color”.
You may find it useful to search through the EnumTraits class in the API.

Here is an example of getting the variant of a block, the blockVariant variable is the same that is shown when you do BlockState#toString. (Note: code untested)

Optional<BlockTrait<?>> variantTrait = block.getTrait("variant");
if (variantTrait.isPresent()) {
    Optional<?> variant = block.getTraitValue(variantTrait.get());
    if (variant.isPresent()) {
        String blockVariant = variant.get().toString();
    }
}
2 Likes

Alternatively, it would be possible to go over all DataManipulators and check for if one extends VariantData. (Code untested)

for(DataManipulator data : block.getContainers()) {
    if (data instanceof VariantData) {
        Object value = ((VariantData) data).type().get();
        String blockVariant = (value instanceof CatalogType) ? ((CatalogType) value).getId() : value.toString();
    }
}

The way the code is written you may experience some weird stuff when there’s more than one VariantData. That can happen for Quartz (as they can have a QuartzData and an AxisData), stairs etc, basically everything that can have both a variant and a direction.

But the Data API way, won’t necessarily work for modded blocks either.

The “variant=diorite” is an inbuilt Mojang concept into the block, which in Sponge is called a trait.

What you really want, is a way for a block to match a user inputtable string right? like in a config?

So you could either test against BlockTypes or BlockStates. However, BlockStates are difficult to work with, as some of their traits that distinguish block states are useless for some uses, take redstone power for a jobs plugin for example. So what we need, is a serializable Predicate Something that can test blocks to see if they match (similar to your regex idea) and be easy to input by users.

Blood suggested that we use the old meta values that blocks had, but all that does is restrict you to dealing with specific blockstates, and you would still need to create a set of blockstates that are aliased under another name, e.g. Leaves would cover blocktypes leaves and leaves2…

The other idea blood had, was that the blockstates be dumped to a file as strings, so it was at least easy to copy them and refer to them in config files, in order to create these sets, This is a somewhat better idea and would be the simplest solution that seems feasible.

However I propose another solution, slightly more complex, but certainly more flexible and easy to use for more advanced tests, resulting in less work for server admins that can understand the concept.

3 Likes

Hello, I apologize for bringing this back to the surface, but I’d like to know how this could be applied to a player holding an item player.getItemInHand(). How can I get the variant from this? Thanks.

Answered here: [Solved] Item In Hand Properties - #4 by gabizou
Docs have a page on manipulating item stacks and how to use Data API.

after 3 years, is there any good way to do this, easy for server admin to edit the config file ?