package net.minecraft.world.level; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; public interface BlockGetter extends LevelHeightAccessor { @Nullable BlockEntity getBlockEntity(BlockPos pos); default Optional getBlockEntity(BlockPos pos, BlockEntityType blockEntityType) { BlockEntity blockEntity = this.getBlockEntity(pos); return blockEntity != null && blockEntity.getType() == blockEntityType ? Optional.of(blockEntity) : Optional.empty(); } BlockState getBlockState(BlockPos pos); FluidState getFluidState(BlockPos pos); default int getLightEmission(BlockPos pos) { return this.getBlockState(pos).getLightEmission(); } default int getMaxLightLevel() { return 15; } default Stream getBlockStates(AABB area) { return BlockPos.betweenClosedStream(area).map(this::getBlockState); } default BlockHitResult isBlockInLine(ClipBlockStateContext context) { return traverseBlocks( context.getFrom(), context.getTo(), context, (clipBlockStateContext, blockPos) -> { BlockState blockState = this.getBlockState(blockPos); Vec3 vec3 = clipBlockStateContext.getFrom().subtract(clipBlockStateContext.getTo()); return clipBlockStateContext.isTargetBlock().test(blockState) ? new BlockHitResult( clipBlockStateContext.getTo(), Direction.getNearest(vec3.x, vec3.y, vec3.z), BlockPos.containing(clipBlockStateContext.getTo()), false ) : null; }, clipBlockStateContext -> { Vec3 vec3 = clipBlockStateContext.getFrom().subtract(clipBlockStateContext.getTo()); return BlockHitResult.miss(clipBlockStateContext.getTo(), Direction.getNearest(vec3.x, vec3.y, vec3.z), BlockPos.containing(clipBlockStateContext.getTo())); } ); } /** * Checks if there's block between {@code from} and {@code to} of context. * This uses the collision shape of provided block. */ default BlockHitResult clip(ClipContext context) { return traverseBlocks(context.getFrom(), context.getTo(), context, (clipContext, blockPos) -> { BlockState blockState = this.getBlockState(blockPos); FluidState fluidState = this.getFluidState(blockPos); Vec3 vec3 = clipContext.getFrom(); Vec3 vec32 = clipContext.getTo(); VoxelShape voxelShape = clipContext.getBlockShape(blockState, this, blockPos); BlockHitResult blockHitResult = this.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); VoxelShape voxelShape2 = clipContext.getFluidShape(fluidState, this, blockPos); BlockHitResult blockHitResult2 = voxelShape2.clip(vec3, vec32, blockPos); double d = blockHitResult == null ? Double.MAX_VALUE : clipContext.getFrom().distanceToSqr(blockHitResult.getLocation()); double e = blockHitResult2 == null ? Double.MAX_VALUE : clipContext.getFrom().distanceToSqr(blockHitResult2.getLocation()); return d <= e ? blockHitResult : blockHitResult2; }, clipContext -> { Vec3 vec3 = clipContext.getFrom().subtract(clipContext.getTo()); return BlockHitResult.miss(clipContext.getTo(), Direction.getNearest(vec3.x, vec3.y, vec3.z), BlockPos.containing(clipContext.getTo())); }); } @Nullable default BlockHitResult clipWithInteractionOverride(Vec3 startVec, Vec3 endVec, BlockPos pos, VoxelShape shape, BlockState state) { BlockHitResult blockHitResult = shape.clip(startVec, endVec, pos); if (blockHitResult != null) { BlockHitResult blockHitResult2 = state.getInteractionShape(this, pos).clip(startVec, endVec, pos); if (blockHitResult2 != null && blockHitResult2.getLocation().subtract(startVec).lengthSqr() < blockHitResult.getLocation().subtract(startVec).lengthSqr()) { return blockHitResult.withDirection(blockHitResult2.getDirection()); } } return blockHitResult; } default double getBlockFloorHeight(VoxelShape shape, Supplier belowShapeSupplier) { if (!shape.isEmpty()) { return shape.max(Direction.Axis.Y); } else { double d = ((VoxelShape)belowShapeSupplier.get()).max(Direction.Axis.Y); return d >= 1.0 ? d - 1.0 : Double.NEGATIVE_INFINITY; } } default double getBlockFloorHeight(BlockPos pos) { return this.getBlockFloorHeight(this.getBlockState(pos).getCollisionShape(this, pos), () -> { BlockPos blockPos2 = pos.below(); return this.getBlockState(blockPos2).getCollisionShape(this, blockPos2); }); } static T traverseBlocks(Vec3 from, Vec3 to, C context, BiFunction tester, Function onFail) { if (from.equals(to)) { return (T)onFail.apply(context); } else { double d = Mth.lerp(-1.0E-7, to.x, from.x); double e = Mth.lerp(-1.0E-7, to.y, from.y); double f = Mth.lerp(-1.0E-7, to.z, from.z); double g = Mth.lerp(-1.0E-7, from.x, to.x); double h = Mth.lerp(-1.0E-7, from.y, to.y); double i = Mth.lerp(-1.0E-7, from.z, to.z); int j = Mth.floor(g); int k = Mth.floor(h); int l = Mth.floor(i); BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(j, k, l); T object = (T)tester.apply(context, mutableBlockPos); if (object != null) { return object; } else { double m = d - g; double n = e - h; double o = f - i; int p = Mth.sign(m); int q = Mth.sign(n); int r = Mth.sign(o); double s = p == 0 ? Double.MAX_VALUE : p / m; double t = q == 0 ? Double.MAX_VALUE : q / n; double u = r == 0 ? Double.MAX_VALUE : r / o; double v = s * (p > 0 ? 1.0 - Mth.frac(g) : Mth.frac(g)); double w = t * (q > 0 ? 1.0 - Mth.frac(h) : Mth.frac(h)); double x = u * (r > 0 ? 1.0 - Mth.frac(i) : Mth.frac(i)); while (v <= 1.0 || w <= 1.0 || x <= 1.0) { if (v < w) { if (v < x) { j += p; v += s; } else { l += r; x += u; } } else if (w < x) { k += q; w += t; } else { l += r; x += u; } T object2 = (T)tester.apply(context, mutableBlockPos.set(j, k, l)); if (object2 != null) { return object2; } } return (T)onFail.apply(context); } } } }