The bug
Summoning any living entity on the wings of the ender dragon (same position as the middle of the wing) will give the entity infinite velocity and crash the server. This is due to a divide by zero case with the wings and a living entity.
How to reproduce
Create a new world
Go to the end
Give yourself a command block and set it to repeat
Enter this command in the command block and power the block:
/tp @e[type=minecraft:ender_dragon] 0.0 70.0 0.0
Summon armor stands on the dragon:
/execute at @e[type=minecraft:ender_dragon] run summon minecraft:armor_stand
→ ❌ The server crashing after a minute or two
Code analysis
Using 1.16.3 yarn mappings.
We will be looking at the EnderDragonEntity
code today.
Let’s go to:
public void tickMovement()
Inside this function there is the launchLivingEntities
function. Let’s explore this.
if (!this.world.isClient && this.hurtTime == 0) {
this.launchLivingEntities(this.world.getOtherEntities(this, this.partWingRight.getBoundingBox().expand(4.0, 2.0, 4.0).offset(0.0, -2.0, 0.0), EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR));
this.launchLivingEntities(this.world.getOtherEntities(this, this.partWingLeft.getBoundingBox().expand(4.0, 2.0, 4.0).offset(0.0, -2.0, 0.0), EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR));
//removed code
}
This function takes in the entities around the dragon wing (will later be limited to Living Entities )
private void launchLivingEntities(List<Entity> entities) {
double d = (this.partBody.getBoundingBox().minX + this.partBody.getBoundingBox().maxX) / 2.0;
double e = (this.partBody.getBoundingBox().minZ + this.partBody.getBoundingBox().maxZ) / 2.0;
for (Entity lv : entities) {
if (!(lv instanceof LivingEntity)) continue;
double f = lv.getX() - d;
double g = lv.getZ() - e;
double h = f * f + g * g;
lv.addVelocity(f / h * 4.0, 0.2f, g / h * 4.0);
//removed stuff below
}
}
From this function call, it takes the wing and finds the middle of it. For the next part, let's say we have an entity at the same location as the middle of the wing and it is a living entity. As the x and z are the same, f
and g
will be zero. As f
and g
are zero, h
will be zero. Now, as h
is zero, the lv.addVelocity
will make the x and z velocity infinite as in java a float divide by zero will make a value infinity.
Comments 4
"in java a float divide by zero will make a value infinity"
That's a pretty broken Java bug. Yes Minecraft should work around it, but that IS a bug on Java's part not Minecraft's part. Divide by zero error is "you have asked for nonsense. No result." It's the same as if you asked what's seven divided by a kitten. It should not result in infinity, it should result in either "error" or "not a number/NaN". NaN is usually misinterpreted as zero in Minecraft, which is why that bug of the bedrock at x0 y0 z0 getting replaced by logs happened back in 1.7/early 1.8.
"in java a float divide by zero will make a value infinity"
"That's a pretty broken Java bug."
It isn't a bug at all, it's working exactly as specified in the floating point standard. (IEEE-754). If it worked in any other way then it would be a bug.
Hi!
I can confirm in 1.16.3.