mojira.dev
MC-123081

Placing an end crystal when entering The End prevents Ender Dragon from spawning

The bug

When entering The End for the first time, immediately placing an end crystal on the obsidian platform will prevent the ender dragon from spawning, and also generate the exit portal, skipping the need for the dragon fight altogether.

How to reproduce

  1. Obtain an end crystal

  2. Enter an active overworld end portal

  3. Place the end crystal as soon as you enter the end dimension

Observed results

The ender dragon will not appear, and the exit portal will be generated already.

Expected results

Placing an end crystal shouldn't prevent the dragon from spawning, and the exit portal should only be generated/activated when the dragon is defeated.

Code analysis

Code analysis by @unknown can be found in this comment.

Related issues

Attachments

Comments

migrated
[media]
migrated

Hey there, recently i checkout the operation, my big friend ender dragon doesn't disappears.

migrated

Still exists in 1.13-pre7. Immediately placing an end crystal on the obsidian platform upon spawning into the end causes no dragon to spawn and the exit portal is generated.

migrated

Confirmed for 1.13.1.

migrated

Affects 1.14.4

migrated

Affects 19w46b

migrated

Relates to MC-105080

Avoma

Can confirm in 20w49a.

violine1101

Still able to reproduce in 21w06a, however I needed to set my render distance to 2 to minimize the lag from the new world generation.

Chandler

Code analysis using 21w05b yarn mappings:

At net.minecraft.entity.boss.dragon.EnderDragonFight, on the first tick/setup that the player is in the end the DragonKilled tag is set to true

public EnderDragonFight(ServerWorld world, long gatewaysSeed, CompoundTag tag) {
        this.world = world;
        if (tag.contains("DragonKilled", 99)) {
            if (tag.containsUuid("Dragon")) {
                this.dragonUuid = tag.getUuid("Dragon");
            }
            this.dragonKilled = tag.getBoolean("DragonKilled");
            this.previouslyKilled = tag.getBoolean("PreviouslyKilled");
            if (tag.getBoolean("IsRespawning")) {
                this.dragonSpawnState = EnderDragonSpawnState.START;
            }
            if (tag.contains("ExitPortalLocation", 10)) {
                this.exitPortalLocation = NbtHelper.toBlockPos(tag.getCompound("ExitPortalLocation"));
            }
        } else {
            this.dragonKilled = true;
            this.previouslyKilled = true;
        }
}

Whenever the End Crystal is placed, the function to respawn the dragon is called to check for the needed end crystals. The function, if called on this first tick, incorrectly reads the DragonKilled tag to generate the portal early.

private void respawnDragon(List<EndCrystalEntity> crystals) {
        if (this.dragonKilled && this.dragonSpawnState == null) {
            BlockPattern.Result lv = this.findEndPortal();
            while (lv != null) {
                for (int i = 0; i < this.endPortalPattern.getWidth(); ++i) {
                    for (int j = 0; j < this.endPortalPattern.getHeight(); ++j) {
                        for (int k = 0; k < this.endPortalPattern.getDepth(); ++k) {
                            CachedBlockPosition lv2 = lv.translate(i, j, k);
                            if (!lv2.getBlockState().isOf(Blocks.BEDROCK) && !lv2.getBlockState().isOf(Blocks.END_PORTAL)) continue;
                            this.world.setBlockState(lv2.getBlockPos(), Blocks.END_STONE.getDefaultState());
                        }
                    }
                }
                lv = this.findEndPortal();
            }
            this.dragonSpawnState = EnderDragonSpawnState.START;
            this.spawnStateTimer = 0;
            this.generateEndPortal(false);
            this.crystals = crystals;
        }
    }

This then causes convertFromLegacy() to see the portal, thus thinking the player has already beat the dragon.

private void convertFromLegacy() {
        LOGGER.info("Scanning for legacy world dragon fight...");
        boolean bl = this.worldContainsEndPortal();
        if (bl) {
            LOGGER.info("Found that the dragon has been killed in this world already.");
            this.previouslyKilled = true;
        } else {
            LOGGER.info("Found that the dragon has not yet been killed in this world.");
            this.previouslyKilled = false;
            if (this.findEndPortal() == null) {
                this.generateEndPortal(false);
            }
        }
    }

This prevents the dragon from respawning, as well as creates an activated end portal.

mattp12

Can confirm for 23w04a.

Moesh

We could not reproduce this in the latest snapshot. Is this still an issue?

tryashtar

I can still reproduce. I've attached a video

[Mojang] Mega_Spud (Jay)

mgustavsson

Confirmed

Important

Mob spawning

ender_crystal, ender_dragon, return-end-portal

Minecraft 1.12.2, Minecraft 17w49b, Minecraft 1.13-pre4, Minecraft 1.13-pre7, Minecraft 1.13.1, ..., 21w06a, 1.17.1, 1.18.1 Release Candidate 2, 23w04a, 23w18a

1.20 Pre-release 1

Retrieved