I made a AI (goal) that zombie break block.
To show the block breaking, I used `destroyBlockProgress` with reference to `BreakDoorGoal`.
It's fine when there's just one or two zombies, but when I summon multiple zombies, it crashes after a few minutes.
crash log: https://gist.github.com/reasure3/9ec90ab2141968535630c5d72558de79
simple code:
public class SetOrBreakBlockGoal extends Goal {
protected PathfinderMob mob;
protected LivingEntity target;
protected Action action;
protected int blockInteractTime;
protected int breakTime;
protected int lastProgress;
protected BlockState block;
protected BlockPos.MutableBlockPos blockPos; <-- This was a problem. not use MutableBlockPos!!
protected long lastStuckCheckTime;
protected Vec3 lastStuckCheckPos;
protected boolean isStuck;
public SetOrBreakBlockGoal(PathfinderMob mob) {
this.mob = mob;
this.action = Action.NONE;
this.blockInteractTime = -1;
this.blockPos = new BlockPos.MutableBlockPos(0, 0, 0);
this.lastProgress = -1;
this.lastStuckCheckTime = mob.level.getGameTime();
this.lastStuckCheckPos = Vec3.ZERO;
this.isStuck = false;
}
@Override
public boolean canUse() {
if (mob.level instanceof ServerLevel level) {
if (MathUtil.getDay(level) >= SpawnConfig.ZOMBIE_SET_OR_BREAK_BLOCLK_DAY.get()) {
target = mob.getTarget();
if (!isRightTarget()) return false;
checkStuck();
return isStuck;
}
}
return false;
}
@Override
public boolean canContinueToUse() {
if (action == Action.NONE) return false;
if (blockInteractTime < 0) return false;
if (isRightTarget()) {
return mob.level.getBlockState(blockPos).is(block.getBlock());
}
return false;
}
@Override
public void start() {
int dy = target.getBlockY() - mob.getBlockY();
if (dy > 1) {
blockPos.set(mob.getBlockX(), mob.getBlockY() + 2, mob.getBlockZ());
block = mob.level.getBlockState(blockPos);
if (block.isAir()) {
// set block code
} else {
block = mob.level.getBlockState(blockPos);
breakTime = getBlockBreakTick();
blockInteractTime = breakTime;
if (breakTime == 0) action = Action.BREAK_INSTANCE;
else if (breakTime < 0) action = Action.NONE;
else action = Action.BREAK;
}
} else action == Action.NONE;
if (action == Action.BREAK && !ForgeHooks.canEntityDestroy(mob.level, blockPos, mob)) {
action = Action.NONE;
}
}
@Override
public void tick() {
if (action == Action.BREAK) {
if (mob.level.random.nextInt(10) == 0 && !mob.swinging) {
mob.swing(mob.getUsedItemHand());
}
// breaking block
int progress = (int) ((float) (breakTime - blockInteractTime) / (float) breakTime * 10.0f);
if (progress != lastProgress) {
mob.level.destroyBlockProgress(mob.getId(), blockPos, progress);
lastProgress = progress;
}
}
// goal is finished
if (blockInteractTime == 0) {
if (action == Action.BREAK_INSTANCE || action == Action.BREAK) {
block = mob.level.getBlockState(blockPos);
BlockEntity blockEntity = block.hasBlockEntity() ? mob.level.getBlockEntity(blockPos) : null;
mob.level.removeBlock(blockPos, false);
// this.mob.level.levelEvent(1021, blockPos, 0);
this.mob.level.levelEvent(2001, blockPos, Block.getId(mob.level.getBlockState(blockPos)));
Block.dropResources(block, mob.level, blockPos, blockEntity, mob, mob.getMainHandItem());
}
}
blockInteractTime--;
}
@Override
public void stop() {
if (action == Action.BREAK)
mob.level.destroyBlockProgress(mob.getId(), blockPos, -1);
action = Action.NONE;
blockInteractTime = -1;
lastProgress = -1;
isStuck = false;
lastStuckCheckTime = mob.level.getGameTime();
lastStuckCheckPos = mob.position();
}
}
full code: https://github.com/reasure3/ZombieSurvivalMod/blob/master/src/main/java/com/reasure/zomsurvival/entity/goal/SetOrBreakBlockGoal.java