Jump to content

[Solved] Get String Representation of IProperty Value


IdrisQe

Recommended Posts

For simpleness' sake, let's say I have something like this:

@SubscribeEvent
public void breakSpeedSwap(PlayerEvent.BreakSpeed event) {
   Block block = event.getState().getBlock();
   String prop = "snowy";
   if ([just pretend it's checking to see if the block is minecraft:grass for this example])
      Main.log("isSnowy: " + block.getBlockState().getProperty(prop).parseValue(prop.getName()).toString());
}

what I want is the value of "minecraft:grass[snowy=???]"
and i want that value in String form, so it can be output or used in String comparisons or whatever. so for a normal everyday grass block, i would want the output to be:

Quote

isSnowy: false


however even with the toString it instead outputs:

Quote

isSnowy: Optional.absent()

for both snowy and non-snowy grass blocks.

So I'm just wondering, how can I properly turn the value of the Optional returned by parseValue into a String?
Am I using the wrong BlockState for this? There's like 10 different ways to get the BlockState from a block and it's kind of overwhelming.
I was originally going to use getDefaultBlockState() but I feel like that would just return, well, the default BlockState with the default values, so it would always be snowy=false? Or am I interpreting what Default means in this case wrong?

Edited by IdrisQe
Solved!
Link to comment
Share on other sites

Look at how vanilla does it in the debug screen.

I assume they do something like

 

blockstate = Minecraft#selectedThing (can’t remember its name, it’s a ray trace result)

for (PropertyKey : blockstate.getPropertyKeys()) {

String = PropertyKey.toString() + “=“ + blockstate.getPropertyValue(PropertyKey).toString();

}

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

GuiOverlayDebug does it like this:

            if (this.mc.objectMouseOver != null && this.mc.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK && this.mc.objectMouseOver.getBlockPos() != null)
            {
                BlockPos blockpos = this.mc.objectMouseOver.getBlockPos();
                IBlockState iblockstate = this.mc.world.getBlockState(blockpos);

                if (this.mc.world.getWorldType() != WorldType.DEBUG_ALL_BLOCK_STATES)
                {
                    iblockstate = iblockstate.getActualState(this.mc.world, blockpos);
                }

                list.add("");
                list.add(String.valueOf(Block.REGISTRY.getNameForObject(iblockstate.getBlock())));
                IProperty<T> iproperty;
                String s;

                for (UnmodifiableIterator unmodifiableiterator = iblockstate.getProperties().entrySet().iterator(); unmodifiableiterator.hasNext(); list.add(iproperty.getName() + ": " + s))
                {
                    Entry < IProperty<?>, Comparable<? >> entry = (Entry)unmodifiableiterator.next();
                    iproperty = (IProperty)entry.getKey();
                    T t = (T)entry.getValue();
                    s = iproperty.getName(t);

                    if (Boolean.TRUE.equals(t))
                    {
                        s = TextFormatting.GREEN + s;
                    }
                    else if (Boolean.FALSE.equals(t))
                    {
                        s = TextFormatting.RED + s;
                    }
                }
            }

            return list;
        }

 

Of course you don't need to do the list part and actually the code is a bit weird cause that string s never seems to be printed out, but you should be able to get the idea -- you should get the "actual state" and you can use the getProperties() from that and iterate through the list.

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

is there any way to easily get the BlockPos and World for getActualState?
The this.mc.objectMouseOver and this.mc.world don't exactly work in this case.

All I really have to work with here is "Block block" or an IBlockState blockState (that was not from getActualState)
I'll just post what I'm actually working with currently and try to explain what it is I'm trying to achieve...
 

It's a check when the player is breaking or has just broken a block (it's used in both places). It checks to see if a block and its properties, all stored as strings, match those in the config. The actual block is stored as a ResourceLocation in order to do those comparisons for the domain and path.
The properties, meanwhile, are stored as Strings in a List which is also within a List.
So it would be like this, using my snowy grass example:
List BreakableDataWhitelist contains:
     ResourceLocation minecraft:grass
List BreakableProperties contains: (here to align its index with the BreakableDataWhitelist blocks for easy pairing of block and its properties)
     List (nested inside) which contains: (one of these is added to BreakableProperties list for each property specified in the config)
          List (nested again) which contains: (they key/value pairings as specified in the config)
               {"snowy", "grass"}

So, if the block itself matches one of the ones in the whitelist (status=true), it then goes to check that its properties also match.
(forgive the logs, those were from my tests to figure out why it wasn't working)
It does this by getting the property keys from the block's default state, and iterating through them.
It then compares each individual Property with the listed ones, checking that the Key and the Value both match what is given in the config.
Since the config keys and values are strings, it compares the Property Name to the string, and converts the Property Value to a string. This is the part I need to get working.
If both match, a counter is increased (numMatches++), then after it has iterated through them all, it compares it to the number of properties in the whitelist entry for that block.
(which I notice I've done wrong, I should be checking the size of the List inside BreakableProperties, not BreakableProperties itself. Oops. I'll fix that in a few minutes.)
If the number matches, that means that all the properties matched up, and since it's already been determined that the block matches up, it returns true, the block is whitelisted.
Of course if it doesn't work then it returns false.

So the part in specific I need to get to work is:

Quote

if (prop.getName() == kvp.get(0) && block.getBlockState().getProperty(prop.getName()).parseValue(prop.getName()).toString() == kvp.get(1))

I've confirmed that getting the property name works fine. But preferably without rewriting huge chunks of this to use the ActualBlockState (which since it's an IBlockState instead of a BlockStateContainer, doesn't allow the use of getProperty(String) which would mess up the part that IS working)
However, I could use that for the first part and ActualBlockState for the second part, and get the Property another way.
But to do that, I need to get the World and the BlockPos, and I can't seem to find a way to get either, much less both, using the information I have at my disposal.
I considered passing them as arguments in isDataWhiteListed() but that isn't working because the events in the places that it's called also don't have methods to get the BlockPos and World. They can pass a BlockState through event.getTargetBlock() and event.getState() respectively, but I'm not sure if those BlockStates have all the information I need in the first place.

I'll try that for now and see if I can get it to work, but if there's a better, cleaner, less convoluted way to do this I would love someone to point it out, because I can't imagine this happening... probably every tick that a block is being harvested for?... per player, is very good for server performance. Sorry if I'm missing plainly obvious things, this is literally my first foray into Minecraft modding and I've never been well-versed in Java because I always found it to be one of the most intimidating programming languages.
         

private boolean isDataWhitelisted(ResourceLocation blockName, Block block) {
    	boolean status;
    	boolean matched;
    	int index = 0;
        for (ResourceLocation r : BreakableDataWhitelist) {
        	status = false;
        	matched = false;
            if (r.equals(blockName))
                status = true;
            if (r.getResourceDomain().equals("*") && r.getResourcePath().equals(blockName.getResourcePath()))
            	status = true;
            if (r.getResourcePath().equals("*") && r.getResourceDomain().equals(blockName.getResourceDomain()))
            	status = true;
            if (r.toString().equals("*:*"))
            	status = true;
            Main.log.info("iDWl Status: " + status);
            if (status == true) {
            	int numMatches = 0;
            	Collection<IProperty<?>> blockProps = block.getDefaultState().getPropertyKeys();
            	for (IProperty prop : blockProps) {
            		for (List<String> kvp : BreakableProperties) {
            			Main.log.info("iDWl Nam: " + prop.getName());
            			Main.log.info("iDWl Val: " + block.getBlockState().getProperty(prop.getName()).parseValue(prop.getName()).toString());
            			if (prop.getName() == kvp.get(0) && block.getBlockState().getProperty(prop.getName()).parseValue(prop.getName()).toString() == kvp.get(1))
                			numMatches++;
            		}
            	}
            	Main.log.info(numMatches + " matches out of " + BreakableProperties.size());
            	if (numMatches == BreakableProperties.size())
            		matched = true;
            }
            index++;
            if (status == true && matched == true)
            	return true;
        }
        return false;
    }
}

 

Link to comment
Share on other sites

Yeah, so when I pass the blockState from other functions, it always turns out false no matter what I do.
An output of false is still better than an output of Optional.absent(), but it's still not true, even when trying to mine a block that does have the property snowy: true
I need to get the ActualBlockState but to do that I need to get the World and the BlockPos.

With what I have available to me here, how would I do that?

Link to comment
Share on other sites

I know they have IBlockState, as I got the IBlockstate through event.getTargetBlock() and event.getState(), but apparently that didn't work for my purposes.

I call isDataWhitelisted() in each; they're all subscribed to seperately, so I need to get both World and BlockPos from each individually.
PlayerEvent.BreakSpeed has a way to get BlockPos but not World. (event.getPos())
BlockEvent.HarvestDropsEvent has both. (event.getPos() event.getWorld())
PlayerEvent.HarvestCheck has neither, however.

HarvestCheck comes first, then BreakSpeed, then HarvestDropsEvent, if I recall. Which means I can't get the values from HarvestDropEvent before the other two run.

The IBlockState provided from each through event.getTargetBlock() or event.getState() didn't work for me, as it doesn't seem to contain that specific block's properties, instead just having what I assume are the defaults for those properties, so for example, if the block I'm harvesting has snowy: true, the IBlockState gotten from each of those seems to have snowy: false because that's just the default.

So I think I need the ActualBlockState but getting that is proving to be difficult given what I have to work with, unless I want to severely overcomplicate things even more than I already have.

However, it's 3 AM and I need to sleep, so I'm going to leave it here for tonight. Thank you for the help, I'm closer than ever to getting this working, despite my sub-par Java knowledge and essentially blank Forge and Minecraft code knowledge.

Link to comment
Share on other sites

What I'm trying to achieve is to make, or rather, alter a system that allows only certain blocks to be mined by hand if they're whitelisted or don't have a most-efficient tool type, otherwise it makes trying to harvest them with the wrong tool incredibly slow and not drop anything, even if it would in Vanilla.
The check being in these three places is something from the source mod I'm working off of, No Tree Punching by alcatrazEscapee.

The entire HarvestCheck area is a kind of double-check he implemented to stop certain blocks from his mod from not giving their drops like they should, even when whitelisted.
since his check is there and in these other two places, I need to put my check there too, to see if they're whitelisted.
My check is different because I have a seperate whitelist that includes properties, so that I can whitelist one variant of a block but not the other, something that is not possible in the original code.
If the isWhitelisted from his original whitelist or my isDataWhitelisted is found to be true in HarvestCheck, it does event.setCanHarvest(true) for the block being mined.
In the BreakSpeed section, it stops the speed from slowing down if either is true. In the HarvestBlockDrops section, it allows them to give their drops if either is true.

In his own words, paraphrased, he said specifically that the HarvestCheck was there to make sure that even blocks that say they need a specific tool to mine properly can be mined at normal speed by hand and get their drops if whitelisted, like the loose rocks from his mod. Without the HarvestCheck, they would be considered unbreakable by hand, even if whitelisted, thereby causing those to not drop.

And I'd rather like to keep that functionality in my variant of the mod if possible, although I suppose it isn't strictly necessary, as I've removed the built-in new blocks to keep the focus to the specific functionality of block-breaking restrictions.

Edited by IdrisQe
Link to comment
Share on other sites

Just to note it here, no further help is required on this issue!

I got it working perfectly! I used raytracing and stuff in HarvestCheck to get the BlockPos, which seemed to work. The issue then was that the block still wouldn't drop because the actual state stops being snowy after the block is broken, which is when HarvestBlockDrops fires. So how did I fix that?

I was wrong, HarvestCheck comes after BreakSpeed, which means I can get the ActualBlockState in BreakSpeed, save it outside the BreakSpeed event call so that the others can use that, which actually solves that problem in addition to greatly simplifying the code! Since I only get the ActualBlockState while the block is still in the correct form, even after it breaks, it still passes as snowy so it matches the whitelist!

 

It works exactly as intended now!

Thank you all for the help! I know I was a bit difficult about this, and sorry for that. I'm still pretty much a Java and Minecraft/Forge code newbie.

Link to comment
Share on other sites

10 hours ago, diesieben07 said:

Note that the block being broken/harvested is not always directly under the player's crosshair (first thing that comes to mind is Veinminer).

Or mining machines or 3x3x3 hammers.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Also what if two players break different blocks at the same time?

>BreakspeedEvent1

>BreakspeedEvent2

>HarvestEvent1

>HarvestEvent2

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

10 hours ago, Cadiboo said:

Also what if two players break different blocks at the same time?

>BreakspeedEvent1

>BreakspeedEvent2

>HarvestEvent1

>HarvestEvent2

There's no problems with what he's doing in this regard.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

15 hours ago, Draco18s said:

There's no problems with what he's doing in this regard.

>BreakspeedEvent1 (cache blockstate 1)

>BreakspeedEvent2 (cache blockstate 2)

>HarvestEvent1 (operate with blockstate 2 - should be with blockstate 1!)

>HarvestEvent2 (operate with blockstate 2)

 

this is could be solved with caching the blockstates in a list in the break speed event and operating on the first element of the list in harvest event.

I’m saying this based on the assumption that IdrisQe is caching the blockstate in a single variable

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

On 9/28/2018 at 12:02 AM, diesieben07 said:

Note that the block being broken/harvested is not always directly under the player's crosshair (first thing that comes to mind is Veinminer).

Don't worry, I'm not checking for that anymore. I found a better way of doing it that means I only need to get the blockstate once, and I can get it from BreakSpeed instead. As a catch for cases that don't trigger BreakSpeed (i don't think it happens in creative, for example) I also added an update to the blockstate on ...  BreakBlock? HarvestBlock? ... the one that fires just BEFORE a block is broken. I'm bad with all these names.
 

And the way I'm implementing this should be compatible with veinminer as it typically only mines the same type of block as the one you actually harvest. (And if it isn't, not a big deal, since Veinminer is kinda antithetical to my intended purpose of this mod and I honestly don't know why someone would use both together.)
 

As for hammers, that's a bit more of a difficult issue but i think the ring of blocks around the original don't quite follow the same breaking logic? I think since I get the target blockstate during the pre-break event... thing... (again, I can't remember its name)  it should perform the check again for each block...? I don't know how hammers work in their code, and each hammer's code probably varies wildly, so compatability will probably always be an issue. That said, I will have to check that because the modpack I made this for has hammers, and ones which are known to be a bit iffy about which blocks they can mine...

 

On 9/28/2018 at 9:39 PM, Cadiboo said:

Also what if two players break different blocks at the same time?

>BreakspeedEvent1

>BreakspeedEvent2

>HarvestEvent1

>HarvestEvent2

This... Aaaalso shouldn't be an issue...? I'm no expert, and I'm certainly not comfortable enough with modding yet to say it definitely wouldn't be, but given what I'm doing, and given that I get the player doing the block breaking each time, I imagine it would work seperately. Pretty sure a new instance of BreakSpeedEvent and HarvestEvent and all those are called each time a block is mined. If it didn't, then in Vanilla you would have it where two players can't break different blocks at the same time without issue.

At least... I think? Knowing what little I know of Minecraft's code anything could be possible.

 

Either way, I intend to use this in a small server modpack. If issues like that arise, I'm sure they'll be noticed, and I can then figure out how to deal with them.
I'd rather not tinker with my code more at the moment as I have every feature I want in it implemented the way I want it and it's the most stable it's been yet, so I think I'll sit on this version for now, unless a problem does definitively come up.

 

 

 

On a related note, however. Am I right in assuming that on a server, all block-breaking handling and stuff like that is handled by the server and only the server?
So like, if a player joins a server and has a mismatching breakable block whitelist, it won't cause desync because the client's config is ignored in favour of the server's?
That's something I've been a bit worried about, especially after I made it so you can change and refresh the whitelist while ingame without restarting minecraft or relogging into the world.

Link to comment
Share on other sites

1 hour ago, Cadiboo said:

>BreakspeedEvent1 (cache blockstate 1)

>BreakspeedEvent2 (cache blockstate 2)

>HarvestEvent1 (operate with blockstate 2 - should be with blockstate 1!)

>HarvestEvent2 (operate with blockstate 2)

 

this is could be solved with caching the blockstates in a list in the break speed event and operating on the first element of the list in harvest event.

I’m saying this based on the assumption that IdrisQe is caching the blockstate in a single variable

^This would also fix \/this

49 minutes ago, IdrisQe said:

As for hammers, that's a bit more of a difficult issue but i think the ring of blocks around the original don't quite follow the same breaking logic? I think since I get the target blockstate during the pre-break event... thing... (again, I can't remember its name)  it should perform the check again for each block...? I don't know how hammers work in their code, and each hammer's code probably varies wildly, so compatability will probably always be an issue. That said, I will have to check that because the modpack I made this for has hammers, and ones which are known to be a bit iffy about which blocks they can mine...

 

49 minutes ago, IdrisQe said:

I'd rather not tinker with my code more at the moment as I have every feature I want in it implemented the way I want it and it's the most stable it's been yet, so I think I'll sit on this version for now, unless a problem does definitively come up.

Here’s some pseudo code of how to implement, it’s really simple

final ArrayList<IBlockState> cachedBlockstates = new ArrayList();


onEvent(final EventYouGetTheBlockstateIn event) {

    final IBlockState blockstate = getBlockState();

    cachedBlockstates.add(blockstate);

}

onHarvestEvent(final HarvestEvent event) {

    if(cachedBlockstates.isEmpty()) return;

    final IBlockState blockstate = cachedBlockStates.get(0);

    cachedBlockstates.remove(0);

    // do your stuff here

}

[code]

Why can’t you just get the blockstate with world.getBlockstate(pos)?

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

8 hours ago, Cadiboo said:

>BreakspeedEvent1 (cache blockstate 1)

>BreakspeedEvent2 (cache blockstate 2)

>HarvestEvent1 (operate with blockstate 2 - should be with blockstate 1!)

>HarvestEvent2 (operate with blockstate 2)

I have seen no code that would result in this behavior.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

17 hours ago, Cadiboo said:

^This would also fix \/this

 

Here’s some pseudo code of how to implement, it’s really simple

 


final ArrayList<IBlockState> cachedBlockstates = new ArrayList();


onEvent(final EventYouGetTheBlockstateIn event) {

    final IBlockState blockstate = getBlockState();

    cachedBlockstates.add(blockstate);

}

onHarvestEvent(final HarvestEvent event) {

    if(cachedBlockstates.isEmpty()) return;

    final IBlockState blockstate = cachedBlockStates.get(0);

    cachedBlockstates.remove(0);

    // do your stuff here

}



 

Why can’t you just get the blockstate with world.getBlockstate(pos)?

 

I... am? Well, I use GetActualBlockState(world,pos) but same idea.
I don't understand why I need to cache them if I get the blockstate of each block that breaks individually, which I currently do.
I do cache a single blockstate currently; that of whichever block is getting broken at the moment... which should be the only one I need.
I first get it when BreakSpeed starts, then I get it again in BlockEvent.BreakEvent which fires just before the harvestevents, so it should be the correct value for any block broken, unless there are breaking methods which skip BreakEvent maybe and go straight to HarvestEvent but... that seems more like it would be a problem on the end of whatever implemented that, since I'm pretty certain they're meant to go together.

Edited by IdrisQe
Accidentally put response inside quote
Link to comment
Share on other sites

Looks like I misunderstood what you were doing, sorry

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.