267 lines
9.3 KiB
Java
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);
|
|
}
|
|
}
|