minecraft-src/net/minecraft/world/level/BlockGetter.java
2025-07-04 03:45:38 +03:00

267 lines
9.3 KiB
Java

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 <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos pos, BlockEntityType<T> 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<BlockState> 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<VoxelShape> 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, C> T traverseBlocks(Vec3 from, Vec3 to, C context, BiFunction<C, BlockPos, T> tester, Function<C, T> 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<Vec3> 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);
}
}