Jump to content

[1.8] Random unexpected result when removing block from World


Blackout

Recommended Posts

Hello everyone,

 

In my mod, I remove block from world using

world.setBlockState(myBlockPos, Blocks.air.getDefaultState(), 2);

when an explosion occurs to prevent any drops.

There is a problem when I try to remove block like redstone dusts or torches, sometimes there still are drops.

I use '2' as flags so normaly game engine doesn't care about order, right ?

 

In fact, when I have 4 redstone dusts above dirt blocks and I remove this 8 blocks, all is fine, all is removed, I have no drops.

But when I have 5 redstone dusts and I remove this 10 blocks (5 redstone dusts + 5 dirts blocks), sometimes 1 or 2 redstone dusts are dropped, sometimes no drops. It is random, I don't know what happens. I really don't think it come from my side :/ And I can't release my mod with this bug :/ Do you have any ideas ?

Link to comment
Share on other sites

Probably what's happening is you are removing the supporting block before the redstone dust and torches. Then it drops as an item before you removed the actual block. I'm not sure what all the flags represent, but I'm sure one of the flags must allow you to remove the block without updating the surrounding blocks.  If not, perhaps you should try removing all the blocks that require a support, then those that don't.

With all due respect, sir: I do, what I do, the way I do it. ~ MacGyver

Link to comment
Share on other sites

perhaps you should try removing all the blocks that require a support

When I do it, that's work but no way lol xD This is for this mod https://www.youtube.com/watch?v=A3_ibDsezwo, and I can't reverse iterate throught the dependency tree to know which I have to remove first xD That's why I must remove blocks without taking care about orders. Why it's work for 4 blocks with dependency and more it doesn't ? :/

 

 

Link to comment
Share on other sites

Sometimes it works, sometimes it doesn't? Could it be that you are trying to remove some of these blocks client side? I've run into that problem before, myself....

 

Also, reverse iteration is hardly needed: just work from the highest y to the lowest. That already eliminates anything on floors - only things on walls could then still be an issue...

 

Is there any way you could maybe remove non-opaque blocks first? Because all the things you mention need to be attached to opaque blocks...

If anyone has a comprehensive, visual guide to GUIs - don't hesitate to message me. They make my head spin.

Link to comment
Share on other sites

I'm not sure what all the flags represent

 

Is in the Javadoc.

1 causes a block update.

2 sends the change to the client.

4 prevents it from being rerendered.

Flags can be added together.

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

Sometimes it works, sometimes it doesn't? Could it be that you are trying to remove some of these blocks client side? I've run into that problem before, myself....

 

Also, reverse iteration is hardly needed: just work from the highest y to the lowest. That already eliminates anything on floors - only things on walls could then still be an issue...

 

Is there any way you could maybe remove non-opaque blocks first? Because all the things you mention need to be attached to opaque blocks...

 

I do this operation serverside only.

It's not a simple reverse iteration :/ Because wall hanging object, bed, and other custom blocks could be afftected :/

My mod must be compatible with other mods ... Imagine a mods which add a diamond non-opaque block which need support from above, it will be droped and replaced, so duplicate.

I can't release my mod with this potential bug :/

Link to comment
Share on other sites

Here's an idea:

1. Scan for and store all ItemEntities in the area.

2. Destroy blocks.

3. Destroy all ItemEntities in the area.

4. Do whatever you do to restore blocks in that really cool video ya got.

5. Restore the stored ItemEntities.

If anyone has a comprehensive, visual guide to GUIs - don't hesitate to message me. They make my head spin.

Link to comment
Share on other sites

Now I'm curious. How exactly did you do that in the video? I'm assuming you get affected block positions from explosion event, but how did you get them to slowly regenerate,  instead of all at once? Did you start a new thread for that?

 

https://github.com/EyZox/ForgeCreeperHeal/tree/1.2.0

Open source ;)

Handler : https://github.com/EyZox/ForgeCreeperHeal/blob/1.2.0/main/java/fr/eyzox/forgecreeperheal/handler/ExplosionEventHandler.java

Handler Processing Class : https://github.com/EyZox/ForgeCreeperHeal/blob/1.2.0/main/java/fr/eyzox/forgecreeperheal/worldhealer/WorldHealer.java

Data Structure : https://github.com/EyZox/ForgeCreeperHeal/blob/1.2.0/main/java/fr/eyzox/timeline/AbstractTimeline.java

 

I use the tick loop, because Minecraft isn't thread safe ;)

Link to comment
Share on other sites

So, I was working on a solution and I came up with this something like this. I'm not sure how you are storing blocks to be removed, but I used a Map<BlockPos, IBlockState>, and an additional map for tile entities.

 

To remove blocks, something like this worked for me:

 

 


    
    public static List<BlockPos> orderBlocks(List<BlockPos> affectedBlockPositions, World worldObj) {

    	final List<BlockPos> airBlocks = Lists.newArrayList();
    	final List<BlockPos> fallingBlocks = Lists.newArrayList();
    	final List<BlockPos> fullBlocks = Lists.newArrayList();
    	final List<BlockPos> nonFullBlocks = Lists.newArrayList();
    	
    	Iterator<BlockPos> iterator = affectedBlockPositions.iterator();
    	while (iterator.hasNext()) {
    		BlockPos pos = iterator.next();
    		Block block = worldObj.getBlockState(pos).getBlock();
    		
    		if (block == Blocks.air) airBlocks.add(pos);
    		else if (block instanceof BlockFalling) fallingBlocks.add(pos);
    		else if (block.isFullBlock()) fullBlocks.add(pos);
    		else nonFullBlocks.add(pos);
    	}

    	airBlocks.addAll(fullBlocks);
    	airBlocks.addAll(fallingBlocks);
    	airBlocks.addAll(nonFullBlocks);
    	
    	return airBlocks;
    }

 

 

Might not be the most efficient way, but it seems to work.

 

For replacing the blocks, it was similar, but just a bit more complicated. Assuming the blocks are stored in a map like above, replacing them like this should work.

 




			Comparator<BlockPos> posComparator = new Comparator<BlockPos>() {

				@Override
				public int compare(BlockPos o1, BlockPos o2) {
					return o1.compareTo(o2);
				}

			};


			List<Map<BlockPos, IBlockState>> ordered = Lists.newArrayList();
			Map<BlockPos, IBlockState> fullBlocks = Maps.newHashMap();
			Map<BlockPos, IBlockState> fallingBlocks = Maps.newTreeMap(posComparator);
			Map<BlockPos, IBlockState> nonFullBlocks = Maps.newHashMap();
			ordered.add(fullBlocks);
			ordered.add(fallingBlocks);
			ordered.add(nonFullBlocks);

			for (Map.Entry<BlockPos, IBlockState> entry : blocks.entrySet()) {

				Block block = entry.getValue().getBlock();

				if (block == Blocks.air) continue;
				else if (block instanceof BlockFalling) fallingBlocks.put(entry.getKey(), entry.getValue());
	    		else if (block.isFullBlock()) fullBlocks.put(entry.getKey(), entry.getValue());
	    		else nonFullBlocks.put(entry.getKey(), entry.getValue());

			}

 

 

If you iterate over the list 'ordered', and then iterate over each map and replace accordingly, you shouldn't have too many problems. Only thing is that doing it like so seems to screw over doors and beds, but it seems you already have a workaround for that. Again, this probably is terribly inefficient, and ugly, but it seemed to work.

With all due respect, sir: I do, what I do, the way I do it. ~ MacGyver

Link to comment
Share on other sites

This is not efficient, not scalable and not custom block compatible.

Watch my code, I use a Factory to build each "healable block". Its allow someone who have create a custom block to create its own factory to make it compatable with my mod.

I can't use falling, non-opaque etc ... because this is not constant rules.

 

Then, with you method, you sort all block in one time when an explosion occurs. This could create a lag spike for massive explosions.

Link to comment
Share on other sites

This is not efficient, not scalable and not custom block compatible.

Watch my code, I use a Factory to build each "healable block". Its allow someone who have create a custom block to create its own factory to make it compatable with my mod.

I can't use falling, non-opaque etc ... because this is not constant rules.

 

Then, with you method, you sort all block in one time when an explosion occurs. This could create a lag spike for massive explosions.

Yeah that's just something I threw together quickly as a proof of concept. I didn't bother making it scalable or compatible with custom blocks since you already had. It got me thinking,  though.  Perhaps for each IHealable, you can require a priority. Then, use that priority to determine removal and replace order. Just a thought, though.

With all due respect, sir: I do, what I do, the way I do it. ~ MacGyver

Link to comment
Share on other sites

I ran in debug mod with breakpoint on each EntityItem constructors.

Except these f*ck**g chickens who spawn eggs entity items each 5 min, I could see where is the problem by studying the stack trace.

 

World.setBlockState call Chunk.setSate call Block.breakBlock call sometimes Block.updateNeightboor ><

So the call to Block.breakBlock in Chunk.setState is the source of this issue.

 

I think I have to create a copy of Chunk.setState method, use reflection to access private Chunk field and then I hope this will be fixed.

 

However, I have an other question if I use reflection.

What is the most performant ? Create a wrapper wich contains private chunk field initialized in constructor with reflection and store it in cache while explosion event processing. Or each time use reflection to get field ?

I think I will ask this on stackoverflow but if someone have the answers, I take it :P

 

 

Link to comment
Share on other sites

I would think that doing it once per explosion then storing it would be faster, since you're only doing it once. You could try both ways, test performance, and if there is no noticeable difference, you could do what you are most comfortable with. For me though, unless the speed benefits contrast in a large manner, I tend to go for whatever I come up with first that works.

With all due respect, sir: I do, what I do, the way I do it. ~ MacGyver

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.



×
×
  • Create New...

Important Information

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