package net.minecraft.world.level; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; 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 { int MAX_BLOCK_ITERATIONS_ALONG_TRAVEL = 16; @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 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.getApproximateNearest(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.getApproximateNearest(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.getApproximateNearest(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); } } } static void forEachBlockIntersectedBetween(Vec3 from, Vec3 to, AABB boundingBox, BlockGetter.BlockStepVisitor stepVisitor) { Vec3 vec3 = to.subtract(from); if (!(vec3.lengthSqr() < Mth.square(0.99999F))) { LongSet longSet = new LongOpenHashSet(); Vec3 vec32 = boundingBox.getMinPosition(); Vec3 vec33 = vec32.subtract(vec3); int i = addCollisionsAlongTravel(longSet, vec33, vec32, boundingBox, stepVisitor); for (BlockPos blockPos2 : BlockPos.betweenClosed(boundingBox)) { if (!longSet.contains(blockPos2.asLong())) { stepVisitor.visit(blockPos2, i + 1); } } } else { for (BlockPos blockPos : BlockPos.betweenClosed(boundingBox)) { stepVisitor.visit(blockPos, 0); } } } private static int addCollisionsAlongTravel(LongSet output, Vec3 from, Vec3 to, AABB boundingBox, BlockGetter.BlockStepVisitor stepVisitor) { Vec3 vec3 = to.subtract(from); int i = Mth.floor(from.x); int j = Mth.floor(from.y); int k = Mth.floor(from.z); int l = Mth.sign(vec3.x); int m = Mth.sign(vec3.y); int n = Mth.sign(vec3.z); double d = l == 0 ? Double.MAX_VALUE : l / vec3.x; double e = m == 0 ? Double.MAX_VALUE : m / vec3.y; double f = n == 0 ? Double.MAX_VALUE : n / vec3.z; double g = d * (l > 0 ? 1.0 - Mth.frac(from.x) : Mth.frac(from.x)); double h = e * (m > 0 ? 1.0 - Mth.frac(from.y) : Mth.frac(from.y)); double o = f * (n > 0 ? 1.0 - Mth.frac(from.z) : Mth.frac(from.z)); int p = 0; BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); while (g <= 1.0 || h <= 1.0 || o <= 1.0) { if (g < h) { if (g < o) { i += l; g += d; } else { k += n; o += f; } } else if (h < o) { j += m; h += e; } else { k += n; o += f; } if (p++ > 16) { break; } Optional optional = AABB.clip(i, j, k, i + 1, j + 1, k + 1, from, to); if (!optional.isEmpty()) { Vec3 vec32 = (Vec3)optional.get(); double q = Mth.clamp(vec32.x, i + 1.0E-5F, i + 1.0 - 1.0E-5F); double r = Mth.clamp(vec32.y, j + 1.0E-5F, j + 1.0 - 1.0E-5F); double s = Mth.clamp(vec32.z, k + 1.0E-5F, k + 1.0 - 1.0E-5F); int t = Mth.floor(q + boundingBox.getXsize()); int u = Mth.floor(r + boundingBox.getYsize()); int v = Mth.floor(s + boundingBox.getZsize()); for (int w = i; w <= t; w++) { for (int x = j; x <= u; x++) { for (int y = k; y <= v; y++) { if (output.add(BlockPos.asLong(w, x, y))) { stepVisitor.visit(mutableBlockPos.set(w, x, y), p); } } } } } } return p; } @FunctionalInterface public interface BlockStepVisitor { void visit(BlockPos blockPos, int i); } }