276 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
	
		
			9.4 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 boolean forEachBlockIntersectedBetween(Vec3 from, Vec3 to, AABB boundingBox, BlockGetter.BlockStepVisitor visitor) {
 | |
| 		Vec3 vec3 = to.subtract(from);
 | |
| 		if (vec3.lengthSqr() < Mth.square(0.99999F)) {
 | |
| 			for (BlockPos blockPos : BlockPos.betweenClosed(boundingBox)) {
 | |
| 				if (!visitor.visit(blockPos, 0)) {
 | |
| 					return false;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			LongSet longSet = new LongOpenHashSet();
 | |
| 			Vec3 vec32 = boundingBox.getMinPosition();
 | |
| 			Vec3 vec33 = vec32.subtract(vec3);
 | |
| 			int i = addCollisionsAlongTravel(longSet, vec33, vec32, boundingBox, visitor);
 | |
| 			if (i < 0) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				for (BlockPos blockPos2 : BlockPos.betweenClosed(boundingBox)) {
 | |
| 					if (!longSet.contains(blockPos2.asLong()) && !visitor.visit(blockPos2, i + 1)) {
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return true;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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 -1;
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return p;
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	public interface BlockStepVisitor {
 | |
| 		boolean visit(BlockPos blockPos, int i);
 | |
| 	}
 | |
| }
 |