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
Obtain an end crystal
Enter an active overworld end portal
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
is duplicated by
relates to
Attachments
Comments


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

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.

Confirmed for 1.13.1.

Affects 1.14.4

Affects 19w46b

Relates to MC-105080
Can confirm in 20w49a.
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.
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.

Can confirm for 23w04a.

We could not reproduce this in the latest snapshot. Is this still an issue?
I can still reproduce. I've attached a video