Skip to content

Use hit location for piercing arrow sorting order#782

Open
HaHaWTH wants to merge 1 commit into
ver/1.21.11from
fix/piercing-arrow-order
Open

Use hit location for piercing arrow sorting order#782
HaHaWTH wants to merge 1 commit into
ver/1.21.11from
fix/piercing-arrow-order

Conversation

@HaHaWTH

@HaHaWTH HaHaWTH commented Jun 17, 2026

Copy link
Copy Markdown
Member

Vanilla bug found during reviewing MC-214546.

Description

AbstractArrow sorted multiple entity hits by the target entity position.

This is definitely incorrect and unexpected behavior for large entities:
An entity with a farther origin pos can have a bounding box that is intersected earlier by the projectile.

Reproduce

    private UUID testArrow;
    private final List<String> order = new ArrayList<>();
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage("Player only.");
            return true;
        }
        World world = player.getWorld();
        order.clear();
        testArrow = null;
        Location p = player.getLocation();
        double startX = p.getX();
        double y = p.getY();
        double z = p.getZ() + 3.0;
        Location start = new Location(world, startX, y + 1.1, z);
        Location zombieLoc = new Location(world, startX + 4.0, y, z);
        Location slimeLoc = new Location(world, startX + 5.0, y, z);
        for (Entity entity : world.getNearbyEntities(start, 12, 8, 12)) {
            if (entity.getScoreboardTags().contains("pierce-order-test")
                    || entity.getScoreboardTags().contains("pierce-order-arrow")) {
                entity.remove();
            }
        }
        world.spawn(slimeLoc, Slime.class, slime -> {
            slime.setSize(8);
            slime.setAI(false);
            slime.setSilent(true);
            slime.setCustomName("A_SLIME");
            slime.setCustomNameVisible(true);
            slime.addScoreboardTag("pierce-order-test");
        });
        world.spawn(zombieLoc, Zombie.class, zombie -> {
            zombie.setAI(false);
            zombie.setSilent(true);
            zombie.setCustomName("B_ZOMBIE");
            zombie.setCustomNameVisible(true);
            zombie.addScoreboardTag("pierce-order-test");
        });
        player.sendMessage("Expected correct order: A_SLIME -> B_ZOMBIE");
        Bukkit.getScheduler().runTaskLater(this, () -> {
            Arrow arrow = world.spawnArrow(
                    start,
                    new Vector(1.0, 0.0, 0.0),
                    8.0f,
                    0.0f
            );
            arrow.setGravity(false);
            arrow.setDamage(0.5);
            arrow.setCritical(false);
            arrow.setPierceLevel(4);
            arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
            arrow.addScoreboardTag("pierce-order-arrow");
            testArrow = arrow.getUniqueId();
        }, 2L);
        Bukkit.getScheduler().runTaskLater(this, () -> {
            player.sendMessage("Observed order: " + String.join(" -> ", order));
            if (order.size() >= 2
                    && order.get(0).equals("A_SLIME")
                    && order.get(1).equals("B_ZOMBIE")) {
                player.sendMessage("PASS");
            } else {
                player.sendMessage("FAIL");
            }
        }, 40L);
        return true;
    }
    @EventHandler
    public void onProjectileHit(ProjectileHitEvent event) {
        if (testArrow == null || !event.getEntity().getUniqueId().equals(testArrow)) {
            return;
        }
        if (event.getHitBlock() != null) {
            Bukkit.broadcastMessage("ProjectileHitEvent block: " + event.getHitBlock().getType());
            return;
        }
        Entity hit = event.getHitEntity();
        if (hit == null) {
            Bukkit.broadcastMessage("ProjectileHitEvent with no hit entity");
            return;
        }
        String name = hit.getCustomName();
        if (name == null) {
            name = hit.getType().name();
        }
        order.add(name);
        Bukkit.broadcastMessage("ProjectileHitEvent entity: " + name);
    }

Run the test code snippet, should see:

Before:
B_ZOMBIE -> A_SLIME

After:
A_SLIME -> B_ZOMBIE

@HaHaWTH HaHaWTH changed the base branch from ver/26.2 to ver/1.21.11 June 17, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant