Fireworks, eggs, snowballs, and splash potions of harming fired from dispensers don't knock back mobs, but they do when thrown by the player or a mob such as a snow golem or witch.
Steps to Reproduce:
Build the setup as shown in the attachment below.
Place some snowballs inside of the dispenser.
Summon a husk on the diamond block.
Use the lever to activate the dispenser and watch the husk closely.
Observed Behavior:
Projectiles fired from dispensers don't deal any knockback to entities.
Expected Behavior:
Projectiles fired from dispensers would deal knockback to entities.
Note
Arrows are not affected by this bug, as they knock back mobs when fired either from a bow or a dispenser.
Code analysis
Code analysis by @unknown can be found in this comment.
Linked issues
is duplicated by 3
relates to 4
Attachments
Comments 43
Please link to this comment in the description
The following is based on decompiled version of Minecraft 1.9 using MCP 9.24 beta. All method and class names are the names used in the decompiled version.
The reason why for this bug is that the net.minecraft.entity.EntityLivingBase.attackEntityFrom(DamageSource, float)
method only knockbacks mobs if the indirect damage has an entity as owner. This is not the case for projectiles fired from a dispenser. The problem is that currently the coordinates of the thrower are used to determine the knockback.
This allows "exploits" like [[Tutorial] Guided & Regular Rocket Launcher in Vanilla Minecraft Using Fireballs! [MC 1.8]|https://www.youtube.com/watch?v=548wxRDL1U8].
A possible solution would be to use the motion of the projectile. This means however that the distance does not determine the strength completely anymore.
The following shows how this could be done, but the motion values very likely need to be adjusted.
// Added this
if (source instanceof net.minecraft.util.EntityDamageSourceIndirect && source.getSourceOfDamage() != null) {
Entity damagingEntity = source.getSourceOfDamage();
double d1 = -damagingEntity.motionX;
double d0;
for (d0 = -damagingEntity.motionZ; d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D)
{
d1 = (Math.random() - Math.random()) * 0.01D;
}
this.attackedAtYaw = (float)(MathHelper.atan2(d0, d1) * (180D / Math.PI) - (double)this.rotationYaw);
this.knockBack(entity, 0.4F, d1, d0);
}
// Replaced this
//if (entity != null)
else if (entity != null)
{
double d1 = entity.posX - this.posX;
double d0;
for (d0 = entity.posZ - this.posZ; d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D)
{
d1 = (Math.random() - Math.random()) * 0.01D;
}
this.attackedAtYaw = (float)(MathHelper.atan2(d0, d1) * (180D / Math.PI) - (double)this.rotationYaw);
this.knockBack(entity, 0.4F, d1, d0);
}
else
{
this.attackedAtYaw = (float)((int)(Math.random() * 2.0D) * 180);
}
Here's a proof video : http://www.youtube.com/watch?v=mkR7gTR30xg