mojira.dev
MC-183990

Group AI of some mobs breaks when their target dies

After a Zoglin is attacked by a horde of silverfish, some will not move after the Zoglin has been killed. If a player moves into the aggro range of the then-immobile silverfish, their AI will resume to normal. Should be easy enough to recreate.

Code analysis

Code analysis by @unknown in this comment

Linked issues

Attachments

Comments 9

Can confirm, their AI seems to completely crash once the Zoglin is dead.

Can confirm in 20w46a.

Can confirm in 20w51a.

Can confirm in 21w03a.

Can confirm in 1.16.5.

Can confirm in 21w15a.

Can confirm in 1.17.1.

The reason this bug happens is that the Target of the silverfish does not get set to null once the entity dies.
Before I go deeper into this, this works for all entities that use setGroupRevenge() for RevengeGoal, and it can be triggered by any entity.
The only reason you would have only noticed this with silverfish is that they only have 1 wander goal, the wander goal checks if they have a Target first, if they do then it won't start the goal. So the silverfish seem mostly dead until another goal gets activated.

Code Analysis - Yarn 22w11a

net.minecraft.entity.mob.SilverfishEntity

protected void initGoals() {
	this.callForHelpGoal = new SilverfishEntity.CallForHelpGoal(this);
	this.goalSelector.add(1, new SwimGoal(this));
	this.goalSelector.add(1, new PowderSnowJumpGoal(this, this.world));
	this.goalSelector.add(3, this.callForHelpGoal);
	this.goalSelector.add(4, new MeleeAttackGoal(this, 1.0, false));
	this.goalSelector.add(5, new SilverfishEntity.WanderAndInfestGoal(this)); // This is the only idle movement goal
	this.targetSelector.add(1, (new RevengeGoal(this)).setGroupRevenge()); // Here you can see `setGroupRevenge()` is used
	this.targetSelector.add(2, new ActiveTargetGoal(this, PlayerEntity.class, true));
}

net.minecraft.entity.mob.SilverfishEntity$WanderAndInfestGoal

public boolean canStart() {
	if (this.mob.getTarget() != null) { // Here it fails due to target not being null
		return false;
	} else if (!this.mob.getNavigation().isIdle()) {
		return false;
	} else {
		/* Unrelated - Removed */
	}
}

This happens because when the entity dies, the silverfish don't update their target, their target is not null but it's also not valid.

Here's an easy fix for this:

net.minecraft.entity.mob.SilverfishEntity$WanderAndInfestGoal (Fixed)

public boolean canStart() {
	if (this.mob.getTarget() != null) {
		if (this.mob.getTarget().isAlive()) return false;
		this.mob.setTarget(null); // Set to null if the target is dead
	}
	if (!this.mob.getNavigation().isIdle()) {
		return false;
	} else {
		/* Unrelated - Removed */
	}
}

 

Another easy way to reproduce this by going into survival with a wolf. Letting one of the silverfish hit you, then go into creative. Once your wolf kills one, they will all attack it and kill it and be frozen just like the zoglin.

Now I said it affects other entities, although the other entities are much harder to notice.
If you do the same experiment with blaze you won't be able to tell until you spawn another zoglin, then you will notice that none of the blaze are helping out. This is how you can test if they are also affected. Here's why:

net.minecraft.entity.ai.goal.RevengeGoal

protected void callSameTypeForRevenge() {
	double d = this.getFollowRange();
	Box box = Box.from(this.mob.getPos()).expand(d, 10.0, d);
	List<? extends MobEntity> list = this.mob.world.getEntitiesByClass(this.mob.getClass(), box, EntityPredicates.EXCEPT_SPECTATOR);
	Iterator var5 = list.iterator();
	while(true) {
		MobEntity mobEntity;
		while(true) {
			if (!var5.hasNext()) return;
			mobEntity = (MobEntity)var5.next();
			if (this.mob != mobEntity
				&& mobEntity.getTarget() == null // FAILS HERE, the blaze won't help because they are already targetting something
				&& (!(this.mob instanceof TameableEntity) || ((TameableEntity)this.mob).getOwner() == ((TameableEntity)mobEntity).getOwner())
				&& !mobEntity.isTeammate(this.mob.getAttacker())) {
				if (this.noHelpTypes == null) break;
				/* Bunch of junk - Removed */
			}
		}
		this.setMobEntityTarget(mobEntity, this.mob.getAttacker());
	}
}

So what mobs does this affect:

  • Bee

  • Blaze

  • Dolphin

  • Drowned

  • Endermite

  • Evoker

  • Illusioner

  • Panda

  • Pillager

  • Rabbit

  • Ravager

  • Shulker

  • Silverfish

  • Vex

  • Vindicator

  • Wolf

  • Zombie

  • ZombifiedPiglin

The fix I suggested does not fix the issue where they stop doing group attacks. So that's only a fix for the silverfish.

To fix this correctly you could just do what you do for attackers in LivingEntity.

net.minecraft.entity.LivingEntity

public void baseTick() {
	/* unrelated - removed */
	super.baseTick();
	/* unrelated - removed */
	if (this.attacker != null) {
		if (!this.attacker.isAlive()) { // If Entity is dead, set it to null
			this.setAttacker((LivingEntity)null);
		} else if (this.age - this.lastAttackedTime > 100) {
			this.setAttacker((LivingEntity)null);
		}
	}
	/* unrelated - removed */
}

net.minecraft.entity.mob.MobEntity (Fixed)

public void baseTick() {
	super.baseTick();
	this.world.getProfiler().push("mobBaseTick");
	if (this.isAlive() && this.random.nextInt(1000) < this.ambientSoundChance++) {
		this.resetSoundDelay();
		this.playAmbientSound();
	}
	if (this.target != null && target.isDead()) { // New if statement for fix
		this.setTarget(null);
	}
	this.world.getProfiler().pop();
}

Working Fix: https://github.com/fxmorin/carpet-fixes/commit/ba1d1446b98f0f2e34eb2c259c323760561f7018

Let's be honest tho, you guys need to move the old entities to the new system anyway, and since the new system is event-driven, it makes this super simple to fix. You just listen for entities dying and check if it's the entity you are targetting.

I would also like to suggest that we rename this bug report since it affects a lot more than just silverfish or zoglins

To put a cherry on top, this affects tons of goals. Not just the silverfish wander goal

fweeby dude

(Unassigned)

Confirmed

Gameplay

Low

Mob behaviour

20w20b, 20w21a, 1.16.1, 20w30a, 20w46a, ..., 1.18.2, 22w11a, 1.19.4, 23w16a, 1.21

Retrieved