package net.minecraft.server; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.collect.Sets; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.function.Predicate; import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class EnderDragonBattle { private static final Logger LOGGER = LogManager.getLogger(); private static final Predicate<Entity> b = IEntitySelector.a.and(IEntitySelector.a(0.0D, 128.0D, 0.0D, 192.0D)); public final BossBattleServer bossBattle; public final WorldServer d; // CraftBukkit PAIL private -> public, rename world private final List<Integer> e; private final ShapeDetector f; private int g; private int h; private int i; private int j; private boolean k; private boolean l; public UUID m; // CraftBukkit PAIL private -> public, rename dragonUUID private boolean n; public BlockPosition o; // CraftBukkit PAIL private -> public, rename portalLocation public EnumDragonRespawn p; // CraftBukkit PAIL private -> public, rename respawnPhase private int q; private List<EntityEnderCrystal> r; public EnderDragonBattle(WorldServer worldserver, NBTTagCompound nbttagcompound) { this.bossBattle = (BossBattleServer) (new BossBattleServer(new ChatMessage("entity.minecraft.ender_dragon", new Object[0]), BossBattle.BarColor.PINK, BossBattle.BarStyle.PROGRESS)).setPlayMusic(true).c(true); this.e = Lists.newArrayList(); this.n = true; this.d = worldserver; if (nbttagcompound.hasKeyOfType("DragonKilled", 99)) { if (nbttagcompound.b("DragonUUID")) { this.m = nbttagcompound.a("DragonUUID"); } this.k = nbttagcompound.getBoolean("DragonKilled"); this.l = nbttagcompound.getBoolean("PreviouslyKilled"); if (nbttagcompound.getBoolean("IsRespawning")) { this.p = EnumDragonRespawn.START; } if (nbttagcompound.hasKeyOfType("ExitPortalLocation", 10)) { this.o = GameProfileSerializer.c(nbttagcompound.getCompound("ExitPortalLocation")); } } else { this.k = true; this.l = true; } if (nbttagcompound.hasKeyOfType("Gateways", 9)) { NBTTagList nbttaglist = nbttagcompound.getList("Gateways", 3); for (int i = 0; i < nbttaglist.size(); ++i) { this.e.add(nbttaglist.e(i)); } } else { this.e.addAll(ContiguousSet.create(Range.closedOpen(0, 20), DiscreteDomain.integers())); Collections.shuffle(this.e, new Random(worldserver.getSeed())); } this.f = ShapeDetectorBuilder.a().a(" ", " ", " ", " # ", " ", " ", " ").a(" ", " ", " ", " # ", " ", " ", " ").a(" ", " ", " ", " # ", " ", " ", " ").a(" ### ", " # # ", "# #", "# # #", "# #", " # # ", " ### ").a(" ", " ### ", " ##### ", " ##### ", " ##### ", " ### ", " ").a('#', ShapeDetectorBlock.a(BlockPredicate.a(Blocks.BEDROCK))).b(); } public NBTTagCompound a() { NBTTagCompound nbttagcompound = new NBTTagCompound(); if (this.m != null) { nbttagcompound.a("DragonUUID", this.m); } nbttagcompound.setBoolean("DragonKilled", this.k); nbttagcompound.setBoolean("PreviouslyKilled", this.l); if (this.o != null) { nbttagcompound.set("ExitPortalLocation", GameProfileSerializer.a(this.o)); } NBTTagList nbttaglist = new NBTTagList(); Iterator iterator = this.e.iterator(); while (iterator.hasNext()) { int i = (Integer) iterator.next(); nbttaglist.add(NBTTagInt.a(i)); } nbttagcompound.set("Gateways", nbttaglist); return nbttagcompound; } public void b() { this.bossBattle.setVisible(!this.k); if (++this.j >= 20) { this.l(); this.j = 0; } if (!this.bossBattle.getPlayers().isEmpty()) { this.d.getChunkProvider().addTicket(TicketType.DRAGON, new ChunkCoordIntPair(0, 0), 9, Unit.INSTANCE); boolean flag = this.k(); if (this.n && flag) { this.g(); this.n = false; } if (this.p != null) { if (this.r == null && flag) { this.p = null; this.e(); } this.p.a(this.d, this, this.r, this.q++, this.o); } if (!this.k) { if ((this.m == null || ++this.g >= 1200) && flag) { this.h(); this.g = 0; } if (++this.i >= 100 && flag) { this.m(); this.i = 0; } } } else { this.d.getChunkProvider().removeTicket(TicketType.DRAGON, new ChunkCoordIntPair(0, 0), 9, Unit.INSTANCE); } } private void g() { EnderDragonBattle.LOGGER.info("Scanning for legacy world dragon fight..."); boolean flag = this.i(); if (flag) { EnderDragonBattle.LOGGER.info("Found that the dragon has been killed in this world already."); this.l = true; } else { EnderDragonBattle.LOGGER.info("Found that the dragon has not yet been killed in this world."); this.l = false; if (this.j() == null) { this.a(false); } } List<EntityEnderDragon> list = this.d.j(); if (list.isEmpty()) { this.k = true; } else { EntityEnderDragon entityenderdragon = (EntityEnderDragon) list.get(0); this.m = entityenderdragon.getUniqueID(); EnderDragonBattle.LOGGER.info("Found that there's a dragon still alive ({})", entityenderdragon); this.k = false; if (!flag) { EnderDragonBattle.LOGGER.info("But we didn't have a portal, let's remove it."); entityenderdragon.die(); this.m = null; } } if (!this.l && this.k) { this.k = false; } } private void h() { List<EntityEnderDragon> list = this.d.j(); if (list.isEmpty()) { EnderDragonBattle.LOGGER.debug("Haven't seen the dragon, respawning it"); this.o(); } else { EnderDragonBattle.LOGGER.debug("Haven't seen our dragon, but found another one to use."); this.m = ((EntityEnderDragon) list.get(0)).getUniqueID(); } } public void a(EnumDragonRespawn enumdragonrespawn) { // CraftBukkit PAIL protected -> public, rename setRespawnPhase if (this.p == null) { throw new IllegalStateException("Dragon respawn isn't in progress, can't skip ahead in the animation."); } else { this.q = 0; if (enumdragonrespawn == EnumDragonRespawn.END) { this.p = null; this.k = false; EntityEnderDragon entityenderdragon = this.o(); Iterator iterator = this.bossBattle.getPlayers().iterator(); while (iterator.hasNext()) { EntityPlayer entityplayer = (EntityPlayer) iterator.next(); CriterionTriggers.n.a(entityplayer, (Entity) entityenderdragon); } } else { this.p = enumdragonrespawn; } } } private boolean i() { for (int i = -8; i <= 8; ++i) { int j = -8; label27: while (j <= 8) { Chunk chunk = this.d.getChunkAt(i, j); Iterator iterator = chunk.getTileEntities().values().iterator(); TileEntity tileentity; do { if (!iterator.hasNext()) { ++j; continue label27; } tileentity = (TileEntity) iterator.next(); } while (!(tileentity instanceof TileEntityEnderPortal)); return true; } } return false; } @Nullable private ShapeDetector.ShapeDetectorCollection j() { int i; int j; for (i = -8; i <= 8; ++i) { for (j = -8; j <= 8; ++j) { Chunk chunk = this.d.getChunkAt(i, j); Iterator iterator = chunk.getTileEntities().values().iterator(); while (iterator.hasNext()) { TileEntity tileentity = (TileEntity) iterator.next(); if (tileentity instanceof TileEntityEnderPortal) { ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.f.a(this.d, tileentity.getPosition()); if (shapedetector_shapedetectorcollection != null) { BlockPosition blockposition = shapedetector_shapedetectorcollection.a(3, 3, 3).getPosition(); if (this.o == null && blockposition.getX() == 0 && blockposition.getZ() == 0) { this.o = blockposition; } return shapedetector_shapedetectorcollection; } } } } } i = this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, WorldGenEndTrophy.a).getY(); for (j = i; j >= 0; --j) { ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection1 = this.f.a(this.d, new BlockPosition(WorldGenEndTrophy.a.getX(), j, WorldGenEndTrophy.a.getZ())); if (shapedetector_shapedetectorcollection1 != null) { if (this.o == null) { this.o = shapedetector_shapedetectorcollection1.a(3, 3, 3).getPosition(); } return shapedetector_shapedetectorcollection1; } } return null; } private boolean k() { for (int i = -8; i <= 8; ++i) { for (int j = 8; j <= 8; ++j) { IChunkAccess ichunkaccess = this.d.getChunkAt(i, j, ChunkStatus.FULL, false); if (!(ichunkaccess instanceof Chunk)) { return false; } PlayerChunk.State playerchunk_state = ((Chunk) ichunkaccess).getState(); if (!playerchunk_state.isAtLeast(PlayerChunk.State.TICKING)) { return false; } } } return true; } private void l() { Set<EntityPlayer> set = Sets.newHashSet(); Iterator iterator = this.d.a(EnderDragonBattle.b).iterator(); while (iterator.hasNext()) { EntityPlayer entityplayer = (EntityPlayer) iterator.next(); this.bossBattle.addPlayer(entityplayer); set.add(entityplayer); } Set<EntityPlayer> set1 = Sets.newHashSet(this.bossBattle.getPlayers()); set1.removeAll(set); Iterator iterator1 = set1.iterator(); while (iterator1.hasNext()) { EntityPlayer entityplayer1 = (EntityPlayer) iterator1.next(); this.bossBattle.removePlayer(entityplayer1); } } private void m() { this.i = 0; this.h = 0; WorldGenEnder.Spike worldgenender_spike; for (Iterator iterator = WorldGenEnder.a((GeneratorAccess) this.d).iterator(); iterator.hasNext(); this.h += this.d.a(EntityEnderCrystal.class, worldgenender_spike.f()).size()) { worldgenender_spike = (WorldGenEnder.Spike) iterator.next(); } EnderDragonBattle.LOGGER.debug("Found {} end crystals still alive", this.h); } public void a(EntityEnderDragon entityenderdragon) { if (entityenderdragon.getUniqueID().equals(this.m)) { this.bossBattle.setProgress(0.0F); this.bossBattle.setVisible(false); this.a(true); this.n(); if (!this.l) { this.d.setTypeUpdate(this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, WorldGenEndTrophy.a), Blocks.DRAGON_EGG.getBlockData()); } this.l = true; this.k = true; } } private void n() { if (!this.e.isEmpty()) { int i = (Integer) this.e.remove(this.e.size() - 1); int j = MathHelper.floor(96.0D * Math.cos(2.0D * (-3.141592653589793D + 0.15707963267948966D * (double) i))); int k = MathHelper.floor(96.0D * Math.sin(2.0D * (-3.141592653589793D + 0.15707963267948966D * (double) i))); this.a(new BlockPosition(j, 75, k)); } } private void a(BlockPosition blockposition) { this.d.triggerEffect(3000, blockposition, 0); WorldGenerator.END_GATEWAY.b(WorldGenEndGatewayConfiguration.a()).a(this.d, this.d.getChunkProvider().getChunkGenerator(), new Random(), blockposition); // CraftBukkit - decompile error } private void a(boolean flag) { WorldGenEndTrophy worldgenendtrophy = new WorldGenEndTrophy(flag); if (this.o == null) { for (this.o = this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, WorldGenEndTrophy.a).down(); this.d.getType(this.o).getBlock() == Blocks.BEDROCK && this.o.getY() > this.d.getSeaLevel(); this.o = this.o.down()) { ; } } worldgenendtrophy.b(WorldGenFeatureConfiguration.e).a(this.d, this.d.getChunkProvider().getChunkGenerator(), new Random(), this.o); // CraftBukkit - decompile error } private EntityEnderDragon o() { this.d.getChunkAtWorldCoords(new BlockPosition(0, 128, 0)); EntityEnderDragon entityenderdragon = (EntityEnderDragon) EntityTypes.ENDER_DRAGON.a((World) this.d); entityenderdragon.getDragonControllerManager().setControllerPhase(DragonControllerPhase.HOLDING_PATTERN); entityenderdragon.setPositionRotation(0.0D, 128.0D, 0.0D, this.d.random.nextFloat() * 360.0F, 0.0F); this.d.addEntity(entityenderdragon); this.m = entityenderdragon.getUniqueID(); return entityenderdragon; } public void b(EntityEnderDragon entityenderdragon) { if (entityenderdragon.getUniqueID().equals(this.m)) { this.bossBattle.setProgress(entityenderdragon.getHealth() / entityenderdragon.getMaxHealth()); this.g = 0; if (entityenderdragon.hasCustomName()) { this.bossBattle.a(entityenderdragon.getScoreboardDisplayName()); } } } public int c() { return this.h; } public void a(EntityEnderCrystal entityendercrystal, DamageSource damagesource) { if (this.p != null && this.r.contains(entityendercrystal)) { EnderDragonBattle.LOGGER.debug("Aborting respawn sequence"); this.p = null; this.q = 0; this.f(); this.a(true); } else { this.m(); Entity entity = this.d.getEntity(this.m); if (entity instanceof EntityEnderDragon) { ((EntityEnderDragon) entity).a(entityendercrystal, new BlockPosition(entityendercrystal), damagesource); } } } public boolean d() { return this.l; } public void e() { if (this.k && this.p == null) { BlockPosition blockposition = this.o; if (blockposition == null) { EnderDragonBattle.LOGGER.debug("Tried to respawn, but need to find the portal first."); ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.j(); if (shapedetector_shapedetectorcollection == null) { EnderDragonBattle.LOGGER.debug("Couldn't find a portal, so we made one."); this.a(true); } else { EnderDragonBattle.LOGGER.debug("Found the exit portal & temporarily using it."); } blockposition = this.o; } List<EntityEnderCrystal> list = Lists.newArrayList(); BlockPosition blockposition1 = blockposition.up(1); Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); while (iterator.hasNext()) { EnumDirection enumdirection = (EnumDirection) iterator.next(); List<EntityEnderCrystal> list1 = this.d.a(EntityEnderCrystal.class, new AxisAlignedBB(blockposition1.shift(enumdirection, 2))); if (list1.isEmpty()) { return; } list.addAll(list1); } EnderDragonBattle.LOGGER.debug("Found all crystals, respawning dragon."); this.a((List) list); } } private void a(List<EntityEnderCrystal> list) { if (this.k && this.p == null) { for (ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.j(); shapedetector_shapedetectorcollection != null; shapedetector_shapedetectorcollection = this.j()) { for (int i = 0; i < this.f.c(); ++i) { for (int j = 0; j < this.f.b(); ++j) { for (int k = 0; k < this.f.a(); ++k) { ShapeDetectorBlock shapedetectorblock = shapedetector_shapedetectorcollection.a(i, j, k); if (shapedetectorblock.a().getBlock() == Blocks.BEDROCK || shapedetectorblock.a().getBlock() == Blocks.END_PORTAL) { this.d.setTypeUpdate(shapedetectorblock.getPosition(), Blocks.END_STONE.getBlockData()); } } } } } this.p = EnumDragonRespawn.START; this.q = 0; this.a(false); this.r = list; } } public void f() { Iterator iterator = WorldGenEnder.a((GeneratorAccess) this.d).iterator(); while (iterator.hasNext()) { WorldGenEnder.Spike worldgenender_spike = (WorldGenEnder.Spike) iterator.next(); List<EntityEnderCrystal> list = this.d.a(EntityEnderCrystal.class, worldgenender_spike.f()); Iterator iterator1 = list.iterator(); while (iterator1.hasNext()) { EntityEnderCrystal entityendercrystal = (EntityEnderCrystal) iterator1.next(); entityendercrystal.setInvulnerable(false); entityendercrystal.setBeamTarget((BlockPosition) null); } } } }