package net.minecraft.world.entity.projectile; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Relative; import net.minecraft.world.entity.monster.Endermite; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class ThrownEnderpearl extends ThrowableItemProjectile { private long ticketTimer = 0L; public ThrownEnderpearl(EntityType entityType, Level level) { super(entityType, level); } public ThrownEnderpearl(Level level, LivingEntity owner, ItemStack item) { super(EntityType.ENDER_PEARL, owner, level, item); } @Override protected Item getDefaultItem() { return Items.ENDER_PEARL; } @Override protected void setOwnerThroughUUID(@Nullable UUID uuid) { this.deregisterFromCurrentOwner(); super.setOwnerThroughUUID(uuid); this.registerToCurrentOwner(); } @Override public void setOwner(@Nullable Entity owner) { this.deregisterFromCurrentOwner(); super.setOwner(owner); this.registerToCurrentOwner(); } private void deregisterFromCurrentOwner() { if (this.getOwner() instanceof ServerPlayer serverPlayer) { serverPlayer.deregisterEnderPearl(this); } } private void registerToCurrentOwner() { if (this.getOwner() instanceof ServerPlayer serverPlayer) { serverPlayer.registerEnderPearl(this); } } @Nullable @Override protected Entity findOwner(UUID entityUuid) { if (this.level() instanceof ServerLevel serverLevel) { Entity entity = super.findOwner(entityUuid); if (entity != null) { return entity; } else { for (ServerLevel serverLevel2 : serverLevel.getServer().getAllLevels()) { if (serverLevel2 != serverLevel) { entity = serverLevel2.getEntity(entityUuid); if (entity != null) { return entity; } } } return null; } } else { return null; } } @Override protected void onHitEntity(EntityHitResult result) { super.onHitEntity(result); result.getEntity().hurt(this.damageSources().thrown(this, this.getOwner()), 0.0F); } @Override protected void onHit(HitResult result) { super.onHit(result); for (int i = 0; i < 32; i++) { this.level() .addParticle( ParticleTypes.PORTAL, this.getX(), this.getY() + this.random.nextDouble() * 2.0, this.getZ(), this.random.nextGaussian(), 0.0, this.random.nextGaussian() ); } if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved()) { Entity entity = this.getOwner(); if (entity != null && isAllowedToTeleportOwner(entity, serverLevel)) { Vec3 vec3 = this.oldPosition(); if (entity instanceof ServerPlayer serverPlayer) { if (serverPlayer.connection.isAcceptingMessages()) { if (this.random.nextFloat() < 0.05F && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); if (endermite != null) { endermite.snapTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot()); serverLevel.addFreshEntity(endermite); } } if (this.isOnPortalCooldown()) { entity.setPortalCooldown(); } ServerPlayer serverPlayer2 = serverPlayer.teleport( new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING) ); if (serverPlayer2 != null) { serverPlayer2.resetFallDistance(); serverPlayer2.resetCurrentImpulseContext(); serverPlayer2.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl(), 5.0F); } this.playSound(serverLevel, vec3); } } else { Entity entity2 = entity.teleport( new TeleportTransition(serverLevel, vec3, entity.getDeltaMovement(), entity.getYRot(), entity.getXRot(), TeleportTransition.DO_NOTHING) ); if (entity2 != null) { entity2.resetFallDistance(); } this.playSound(serverLevel, vec3); } this.discard(); } else { this.discard(); } } } private static boolean isAllowedToTeleportOwner(Entity entity, Level level) { if (entity.level().dimension() == level.dimension()) { return !(entity instanceof LivingEntity livingEntity) ? entity.isAlive() : livingEntity.isAlive() && !livingEntity.isSleeping(); } else { return entity.canUsePortal(true); } } @Override public void tick() { int i = SectionPos.blockToSectionCoord(this.position().x()); int j = SectionPos.blockToSectionCoord(this.position().z()); Entity entity = this.getOwner(); if (entity instanceof ServerPlayer serverPlayer && !entity.isAlive() && serverPlayer.serverLevel().getGameRules().getBoolean(GameRules.RULE_ENDER_PEARLS_VANISH_ON_DEATH)) { this.discard(); } else { super.tick(); } if (this.isAlive()) { BlockPos blockPos = BlockPos.containing(this.position()); if ((--this.ticketTimer <= 0L || i != SectionPos.blockToSectionCoord(blockPos.getX()) || j != SectionPos.blockToSectionCoord(blockPos.getZ())) && entity instanceof ServerPlayer serverPlayer2) { this.ticketTimer = serverPlayer2.registerAndUpdateEnderPearlTicket(this); } } } private void playSound(Level level, Vec3 pos) { level.playSound(null, pos.x, pos.y, pos.z, SoundEvents.PLAYER_TELEPORT, SoundSource.PLAYERS); } @Nullable @Override public Entity teleport(TeleportTransition teleportTransition) { Entity entity = super.teleport(teleportTransition); if (entity != null) { entity.placePortalTicket(BlockPos.containing(entity.position())); } return entity; } @Override public boolean canTeleport(Level fromLevel, Level toLevel) { return fromLevel.dimension() == Level.END && toLevel.dimension() == Level.OVERWORLD && this.getOwner() instanceof ServerPlayer serverPlayer ? super.canTeleport(fromLevel, toLevel) && serverPlayer.seenCredits : super.canTeleport(fromLevel, toLevel); } @Override protected void onInsideBlock(BlockState state) { super.onInsideBlock(state); if (state.is(Blocks.END_GATEWAY) && this.getOwner() instanceof ServerPlayer serverPlayer) { serverPlayer.onInsideBlock(state); } } @Override public void onRemoval(Entity.RemovalReason reason) { if (reason != Entity.RemovalReason.UNLOADED_WITH_PLAYER) { this.deregisterFromCurrentOwner(); } super.onRemoval(reason); } @Override public void onAboveBubbleColumn(boolean downwards, BlockPos pos) { Entity.handleOnAboveBubbleColumn(this, downwards, pos); } @Override public void onInsideBubbleColumn(boolean downwards) { Entity.handleOnInsideBubbleColumn(this, downwards); } }