mojira.dev
MC-73302

Block collision box issues (mobs glitch through blocks and more)

This post is about not correctly updated collision boxes of certain blocks.
It describes more than one issue but they are caused by the same problematic part of code and should be seen as one in order to decide on a good solution for them.
When it comes to explanation close to the code I will use MCP names. They should mostly speak for themselves.
As Searge just recently tweeted about it: I will try to keep the posted code to a minimum and give just the needed context for my fix (It's pretty much just the added code). I obviously don't intent to make any of the (especially with MCP) decompiled code public.

I will try to have a look on here in case there are any questions.

1. Overview of bugs and exploits caused by this issue
2. Description of the cause
3. Attempt to fix it (not complete)
4. Ways to reproduce
5. Other posts on the bug tracker referring to it
6. Item elevator bug reimplementation!
7. Affected blocks
8. TLDR

1. Overview of bugs and exploits caused by this issue

It causes the following wrong behaviors:

  • Entities glitching into certain blocks randomly (e.g. into slabs)

  • Pistons not pushing entities correctly (e.g. in elevators)

  • Rendering issues (e.g. normal slab renders as upside down for a split second)

  • Items/XPOrbs jumping up when on certain blocks (e.g. cauldron, stairs)

Fixing it will probably also break the following by the community used glitches:

  • The most common wireless redstone

  • test137e29's simple item elevator (This thing: Item elevator)

The item elevator is a default design used a lot by the technical community so it might be worth it to make it still work by design when the fix breaks it. I'll add a section about that at the very end of the post. Please consider it.

2. Description of the cause

I will try to explain it on the example of into blocks glitching entities.
In Block.java we got 6 integer fields (minX, minY, minZ, maxX, maxY, maxZ).
The following method sets these values. It is overridden by all blocks that can have changeable collision boxes (e.g. slabs).

Block.java

public void setBlockBoundsBasedOnState(IBlockAccess access, BlockPos pos)

The usual way this is use is like that (let's imagine some method in Block.java):

Block.java (random example, how a use could look)

public class Block {
	void doSomethingWithBlocks(World worldObj, BlockPos pos) {
		setBlockBoundsBasedOnState(worldObj, pos);
		// work with  minX, minY, minZ, maxX, maxY and/or maxZ
		if (this.minY < 10) {
			// do something
		} else {
			// do something else
		}
	}
}

Here we can already see the first risk this holds. It's not directly a bug but calls for creating bugs: What if you forget to call setBlockBoundsBasedOnState() before working with the values?
This case commonly happed. For example for chest, anvils and moving blocks. (These are just from experience, I didn't look it up. So forgive me if I'm mistaken πŸ™‚)
See the ways to reproduce section for more detailed examples.
This is also what's used to create wireless redstone.

While this is not really a bug I guess it's still not what you want. Especially when it comes to the plugin API. The system works for blocks with simple AABBs. But by now Minecraft is not limited to that.

The second issue is less visible but you might guess it when I bring up that by now in Minecraft SSP two threads run. One acting as a client and one as a local server.
Both of these threads have access to Block.java. Both work with it.

A common case is that the client thread uses setBlockBoundsBasedOnState() to set the block bounds for the renderer.
An other common case is that the server thread uses it in order to calculate entity collisions.

If both of these happen at the same time the values are changed by the other thread and therefor can cause entities to glitch into blocks or the renderer to show wrong things.

3. Attempt to fix it (not completely)

In the long run it should be completely gotten rid of the setBlockBoundsBasedOnState() and the 6 integer values in Block.java. This design just doesn't work well with multi threading and making all code that uses the on top described pattern synchronized is not really an option.
This needs a lot of refactoring and therefor takes a lot of time though.
I limited myself to explain my idea how to fix the collision issues with entities glitching into blocks.

First up I added a few methods for convenience here and there:

Block.java (added stuff)

public class Block {
	// BoundingBox for a full block
	protected static final AxisAlignedBB AABB_FULL = new AxisAlignedBB(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);

	/**
	 * Adds the bounding box (toAdd) to the list if it intersects with mask.
	 * 
	 * @param list	List it might get added to.
	 * @param toAdd	BoundingBox that should get added. null won't be added.
	 * @param mask	BoundingBox that should intersect with it
	 * @return		true if added, false else (it's not needed atm but it's always usefull to get some feedback)
	 */
	protected static boolean addAABBToList(List list, AxisAlignedBB toAdd, AxisAlignedBB mask) {
		if (toAdd != null && mask.intersectsWith(toAdd)) {
			list.add(toAdd);
			return true;
		}
		return false;
	}

AxisAlignedBB.java (added stuff)

public class AxisAlignedBB {
	/**
	 * Returns a new AABB moved by offset
	 * 
	 * @param offset	The offset it should get moved by. 
	 * @return		A new AABB moved by offset
	 */
	public AxisAlignedBB offset(BlockPos offset) {
		return new AxisAlignedBB(this.minX + offset.getX(), this.minY + offset.getY(), this.minZ + offset.getZ(), this.maxX + offset.getX(), this.maxY + offset.getY(), this.maxZ + offset.getZ());
	}
}

Now the following method should be overridden for all affected blocks that have a simple AABB (with that I mean that they don't need several AABBs).

Block.java

public AxisAlignedBB getCollisionBoundingBox(World worldIn, BlockPos pos, IBlockState state)

For example that would be slabs, chests and anvils.

It should be overridden to work independent from setBlockBoundsBasedOnState() and the 6 integer values in Block.java.

For slabs you can nicely add the possible AABBs to the property enum.

BlockSlab.java (added stuff)

public abstract class BlockSlab extends Block {
	@Override
	public AxisAlignedBB getCollisionBoundingBox(World worldIn, BlockPos pos, IBlockState state) {
		if(this.isDouble()) {
			// The AABB we defined in Block.java
			return AABB_FULL.offset(pos);
		} else {
			return ((BlockSlab.EnumBlockHalf)state.getValue(HALF_PROP)).getAABB().offset(pos);
		}
	}

	public static enum EnumBlockHalf implements IStringSerializable {
		TOP("top", new AxisAlignedBB(0.0F, 0.5F, 0.0F, 1.0F, 1.0F, 1.0F)),
		BOTTOM("bottom", new AxisAlignedBB(0.0F, 0.0F, 0.0F, 1.0F, 0.5F, 1.0F));
		private final String halfName;
		private final AxisAlignedBB AABBForState;

		private EnumBlockHalf(String name, AxisAlignedBB AABBForState) {
			this.halfName = name;
			this.AABBForState = AABBForState;
		}

		public AxisAlignedBB getAABB() {
			return this.AABBForState;
		}

		public String toString() {
			return this.halfName;
		}

		public String getName() {
			return this.halfName;
		}
	}
}

For blocks which can need more than one AABB we want to override this method:
Block.java

Block.java

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List list, Entity collidingEntity)

It's for example called when the collision check of an entity is done to build a list of blocks which might collide with it.
The implementation in Block.java just calls getCollisionBoundingBox() and adds this AABB to the list. That's why we can override getCollisionBoundingBox() for simple blocks.

But I want to show the example of a fence too. For fences I added a few static fields which contain all possibly needed AABBs.

BlockFence.java (added stuff)

public class Block {
	protected static final AxisAlignedBB AABB_POST = new AxisAlignedBB(0.375, 0.0, 0.375, 0.625, 1.5, 0.625);
	protected static final AxisAlignedBB AABB_NORTH = new AxisAlignedBB(0.375, 0.0, 0.0, 0.625, 1.5, 0.625);
	protected static final AxisAlignedBB AABB_EAST = new AxisAlignedBB(0.375, 0.0, 0.375, 1.0, 1.5, 0.625);
	protected static final AxisAlignedBB AABB_SOUTH = new AxisAlignedBB(0.375, 0.0, 0.375, 0.625, 1.5, 1.0);
	protected static final AxisAlignedBB AABB_WEST = new AxisAlignedBB(0.0, 0.0, 0.375, 0.625, 1.5, 0.625);
	protected static final AxisAlignedBB AABB_NORTHSOUTH = AABB_NORTH.union(AABB_SOUTH);
	protected static final AxisAlignedBB AABB_EASTWEST = AABB_EAST.union(AABB_WEST);

	@Override
	public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List list, Entity collidingEntity) {
		// func_176524_e() tells if it should connect to the other block
		boolean connectedNorth = this.func_176524_e(worldIn, pos.offsetNorth());
		boolean connectedSouth = this.func_176524_e(worldIn, pos.offsetSouth());
		boolean connectedWest = this.func_176524_e(worldIn, pos.offsetWest());
		boolean connectedEast = this.func_176524_e(worldIn, pos.offsetEast());

		// Not connected at all? Add AABB_POST and return
		if (!connectedNorth && !connectedSouth && !connectedEast && !connectedWest) {
			Block.addAABBToList(list, AABB_POST.offset(pos), mask);
			return;
		}

		if (connectedNorth && connectedSouth) {
			Block.addAABBToList(list, AABB_NORTHSOUTH.offset(pos), mask);
		} else {
			if (connectedNorth) {
				Block.addAABBToList(list, AABB_NORTH.offset(pos), mask);
			} else if (connectedSouth) {
				Block.addAABBToList(list, AABB_SOUTH.offset(pos), mask);
			}
		}
	      
		if (connectedEast && connectedWest) {
			Block.addAABBToList(list, AABB_EASTWEST.offset(pos), mask);
		} else {
			if (connectedEast) {
				Block.addAABBToList(list, AABB_EAST.offset(pos), mask);
			} else if (connectedWest) {
				Block.addAABBToList(list, AABB_WEST.offset(pos), mask);
			}
		}
	}
}

For other blocks like stairs or moving blocks it might get more complicated but as the functionality is already in there, it just needs to be refactored a bit.

In order to fix all issues with it (for example bouncing items would still happen in some cases) and to completely get rid of the setBlockBoundsBasedOnState() obviously more work is required.

4. Ways to reproduce

Video showing examples of the main issues

Reproducing the first issue is quite easy:
1. Place a double chest.
2. Walk against it from the smaller side. Keep walking against it in the next steps.
3. Look first at the chest closer to you and then at the one further away (by looking at it you update the values in Block.java).
4. You should notice that you move forward and get set back all the time.

Same works even better with anvils:
1. Place two anvils in a row. One facing north-south, the other one east-west.
2. Walk against one of them. And keep walking in the next steps.
3. Look at one then at the other and then at the first anvil again.
4. You should walk through it now.

A good way to reproduce the entities glitching into blocks (threading issue) is the following:
1. Create a new superflat world with the following string: β€œ3;minecraft:bedrock,20*minecraft:stone_slab;1;” (You can use other affected blocks than slabs.)
2. Place a upside down slab somewhere in the world.
3. Place a armor stand (or an other entity) on top of it. (For fences you want to have a water stream pushing a mob against it.)
4. Now set the render distance to a low and then to a high value again (This is to make it more likely by as it causes updates on the blocks).
5. You should be able to see two things. First up if you look at the upside down slab it's hitbox will constantly change to a normal slab and back. Secondly the armor stand falls through the slab.

Pistons not pushing entities:
1. Build up whats shown in this screenshot.
2. Stand where I stand in the screenshot and look at the sideways piston.
3. Place a torch where I look at in the screenshot.
4. You will find that you don't the pushed up. If you make the sideways facing piston look up you will get pushed up.

The rendering issues rarely happen. You can force them by having a lot of the affected blocks around. But as they are a less important issue and somewhat hard to reproduce I will just leave it at that.

Items jumping up is easy to reproduce again:
1. Place a stair
2. Throw an item into the cut out part.
3. It jumps away.

Same with cauldrons and other affected blocks. The item elevator linked earlier is also powered by this issue / a way to reproduce it.

5. Other posts on the bug tracker referring to it

MC-69007, MC-67127, MC-67896, MC-68578, MC-69797 and MC-57152 describe the same issue as far as I can tell.
A part of MC-1836 describes this issue.

It's likely that MC-2025 and/or some duplicates of it describe mobs glitching through fences. It's not unlikely that it is due to this bug.
Also in the duplicates of MC-10 are a few posts were mobs really glitch through fences (which is likely this issue).

There is probably more issues connected to it but I spent enough time browsing the bugtracker for now.
If one of the bad-cop-mods feels like linking the issues to this one that would be great πŸ™‚

6. Item elevator bug reimplementation!

If the partial fix I showed is used (or something similar) then the simple item elevator can be fixed by adding setBlockBoundsBasedOnState() to the method which makes items fly out of blocks in the right spot.

Entity.java (change)

public abstract class Entity implements ICommandSender {
	protected boolean pushOutOfBlocks(double x, double y, double z) {
		BlockPos var7 = new BlockPos(x, y, z);
		double var8 = x - (double)var7.getX();
		double var10 = y - (double)var7.getY();
		double var12 = z - (double)var7.getZ();
		List var14 = this.worldObj.func_147461_a(this.getEntityBoundingBox());

		// added to keep the item elevator for now
		this.worldObj.getBlockState(var7).getBlock().setBlockBoundsBasedOnState(this.worldObj, var7);

		if(var14.isEmpty() && !this.worldObj.func_175665_u(var7)) {
			return false;
		} else {
			...
		}
	}
}

In case you completely get rid of the old system now or some time in future please still consider to leave the fence item elevators working by adding a few lines in this method.
I would be killed by the technical community if I didn't at least try to make you keep the item elevators πŸ™‚

7. Affected blocks

This issue pretty much affects all blocks which have a changeable collision box or a collision box which consists out of more than one bounding box.
This list is probably not complete but I'll give my best. Also the effect of this issue might not show the same for all of them.

The following blocks are probably affected by this glitch in some way:
Snow layers, cobble wall, mossy cobble wall, ladders, iron bars, glass panes, heads, stairs, slabs, hopper, wooden trapdoors, iron trapdoors, all doors, cactus, anvils, chests, trapped chests, brewing stand, all fences, all fence gates, extended pistons, extending pistons, end portal frames, cake and cauldrons.

8. TLDR

Wow, that got long.

In short: For each block just one global Bounding Box is saved in 6 integer values. They have to be set dependent on the state of a block in a certain position before anything is done with them.
This calls for coding bugs and causes threading issues.

I will also make a video explaining the basics. It might take a week till I find time for it though. <Link will follow>

Thanks for reading. Forgive me all the typos, it's late πŸ™‚

Related issues

Attachments

Comments

migrated
[media][media][media][media]
Panda4994

Screenshot with debug screen which was made directly after the video (linked in the text). I forgot to open it in the video.
Shows that it was done in 1.8.1_pre2 and the setup for the easy to reproduce things.

kumasasa

πŸ‘ for a well done bug report !

migrated

Yay Kumasasa is here \ΓΆ/
If I may ask the "nicest bad cop" };] of Mojira for this:

5. Other posts on the bug tracker referring to it
MC-69007, MC-67127, MC-67896, MC-68578, MC-69797 and MC-57152 describe the same issue as far as I can tell.
A part of MC-1836 describes this issue.
It's likely that MC-2025 and/or some duplicates of it describe mobs glitching through fences. It's not unlikely that it is due to this bug.
Also in the duplicates of MC-10 are a few posts were mobs really glitch through fences (which is likely this issue).
There is probably more issues connected to it but I spent enough time browsing the bugtracker for now.
If one of the bad-cop-mods feels like linking the issues to this one that would be great πŸ™‚

}=)
Kind regards,
Meri

kumasasa

@@unknown et al.: Done.

migrated

@@unknown You're the bestestest bad cop in the world <3

migrated

Not sure if this is the same problem so i commented rather than making a new bug report, but I have screenshot of entities getting pushed into complete block walls, which may mean this problem includes solid blocks too. (although I'm not 100% sure it's the same problem).

Panda4994

@Talon2863 It's possible that this issue includes solid blocks in some way.
A while back mobs glitched through wooden floors when you looked at item frames because the renderer changed the collision box of wood to render item frames.
What kind of solid blocks is it? Maybe there is some cross connection there. Even though I doubt it.

I also just found MC-1635 which is describing the issue for chests and anvils.

migrated

@Panda Well, I have a villager house and every so often i would find villagers in the walls. The bottom half was a sideways log and the top block was a glass block. It also happened where I would just hear the villager taking damage which I presume was them taking damage in the walls which was a sideways log on bottom and a plank on top (all oak). It seemed to happen when the house was crowded (due to nighttime and no "safe house") and i logged out or reloaded the chunks in some way.

So, if it is a separate issue I'll make a topic, but it is so difficult to replicate I'm not sure there would be any point. That's why I commented on this topic.

Sonicwave

Not sure if this is the same issue, but in 1.8.1 after logging out and back in, a zombie that was behind a door got through it (without breaking the door) and killed all of the villagers inside.

Panda4994

@@unknown
Hmm wood blocks might be affected. You may remember when item frames where added looking at them made you drop through wooden floors because the hitbox of wood was used for the rendering of the item frames. So that was this bug. There might still be something like that.

@@unknown
It's likely. Doors can for sure be affected.

migrated

This post seems to explain a plethora of long-standing issues in a very concise way. If this is actually the source of as many bugs as it really appears and it gets fixed properly then I can't even imagine all the random problems it would solve for my 3 year-old vanilla server. Fully enchanted pickaxes and players randomly falling through slabs into the void, invaluable or completely irreplaceable villagers escaping from their cells, player elevators actually working finally, item entities moving in a predictable way, farms and machines not losing 15% of their contents, basic chicken farms everyone has not clogging up over time and crashing the machine... Many, many hours of player frustration and lost time on my server likely due directly to this issue. It would be like a brand new game for both the technical and casual community because new players can't even reliably fence their animals in a pen like one would expect or keep their pets from sliding into chests/staircases/etc and dying inside the wall. Myself being pretty hardcore, I do really hope item elevators could stay alive since they're so widely-used and elegant but I'd rather sacrifice them if necessary for the game to become so much more predictable and reliable. We have dozens on our server but I'm not above attempting to replace them all with some new slimeblock contraptions that we've been graced by the gods with previously.

migrated

It's back???

Erik Broes

Long write up and you are absolutely right. It has been bugging me for some time now but the surgery needed to change this code is quite massive.

But! I actually found this while searching for a ticket that referenced the faulty behavior of entities ontop of moving piston blocks because I just fixed that.

And .... I did the massive surgery and it will appear in a snapshot near you soon.

So expect this one to be closed in the near future πŸ™‚

migrated

For the record, that's probably MC-1230.

Panda4994

@unknown Awesome news! Thanks for the heads up! πŸ™‚

migrated

I think this fix breaks some mob farms which rely on pistons moving slabs causing the mobs to fall through into a killing pit. (For instance, this one https://www.youtube.com/watch?v=B8e1rEHNtUE ) Is that a desired outcome?

migrated

This was never intended to exist, and all creations build around it were in a danger zone as this was never intended to happen

Panda4994

@unknown
I found a few minor issues probably related to this fix: MC-88863 and MC-88870.

Also for block 36 there is an issue where it rather uses the hit box of the block than the collision box, I think.
So if you have something standing on top of fences and move the fences sideways with a piston below it, it falls through. (Because the block 36 of the fence is just 1.0 blocks tall, not 1.5 as the collision box)
This might not be new, but I guess it is still related to the change.

Surprisingly few bugs for the far reaching change though πŸ˜›

migrated

maybe the remaining bug reports that are describing behaviour caused by this issue should be closed as well (all bugs in section 5, for example MC-10)

migrated

Doesn't this majorly glitch out item elevators? Not saying it should be un-fixed, as I'm pretty sure it also fixes items glitching all over the place, which caused an insane amount of lag on my old computer.

migrated

If you're referring to test137E29's item elevator: Yes this bugpost fixed it and other issues related to it (there are alternate item elevators by now btw).
The post opener Panda made a video about it back then, as it was unbearable from our view for Devs, as there was a lot hate in the community against them that he had to address.

migrated

Confirmed for 16w41a.. maybe?
I just wanted to test if maxEntityCramming is fixed now so I spawned some with spawn eggs into an endstone tube, and then went on to test redstone-related stuff, and all of a sudden Villagers ran over my setup.

I think what happened was: There were also many Villager-children inside that 1-spot-space, they were cluttered into the very edges of that space, and when they grew up, they glitched through the blocks..?

Not sure if it is a bug and in case it's the same as this bugpost, if it can be fixed etc.

(Edit: Attached 2 screenshots.)

SunCat

@unknown, your issue is MC-103313

Panda4994

Erik Broes

Unconfirmed

Minecraft 1.8, Minecraft 1.8.1-pre2, Minecraft 1.8.1-pre3, Minecraft 1.8.1, Minecraft 1.8.3, ..., Minecraft 15w35e, Minecraft 15w36b, Minecraft 15w36c, Minecraft 15w36d, Minecraft 15w37a

Minecraft 15w38a

Retrieved