package net.minecraft.server.level; import com.google.common.annotations.VisibleForTesting; import java.util.function.Consumer; import net.minecraft.world.level.ChunkPos; public interface ChunkTrackingView { ChunkTrackingView EMPTY = new ChunkTrackingView() { @Override public boolean contains(int x, int z, boolean includeOuterChunksAdjacentToViewBorder) { return false; } @Override public void forEach(Consumer action) { } }; static ChunkTrackingView of(ChunkPos center, int viewDistance) { return new ChunkTrackingView.Positioned(center, viewDistance); } /** * Calculates the chunks that the player needs to drop in the {@code oldChunkTrackingView} and the chunks that need to be sent for the {@code newChunkTrackingView}. The chunks that overlap in both views can be kept. */ static void difference( ChunkTrackingView oldChunkTrackingView, ChunkTrackingView newChunkTrackingView, Consumer chunkMarker, Consumer chunkDropper ) { if (!oldChunkTrackingView.equals(newChunkTrackingView)) { if (oldChunkTrackingView instanceof ChunkTrackingView.Positioned positioned && newChunkTrackingView instanceof ChunkTrackingView.Positioned positioned2 && positioned.squareIntersects(positioned2)) { int i = Math.min(positioned.minX(), positioned2.minX()); int j = Math.min(positioned.minZ(), positioned2.minZ()); int k = Math.max(positioned.maxX(), positioned2.maxX()); int l = Math.max(positioned.maxZ(), positioned2.maxZ()); for (int m = i; m <= k; m++) { for (int n = j; n <= l; n++) { boolean bl = positioned.contains(m, n); boolean bl2 = positioned2.contains(m, n); if (bl != bl2) { if (bl2) { chunkMarker.accept(new ChunkPos(m, n)); } else { chunkDropper.accept(new ChunkPos(m, n)); } } } } } else { oldChunkTrackingView.forEach(chunkDropper); newChunkTrackingView.forEach(chunkMarker); } } } default boolean contains(ChunkPos chunkPos) { return this.contains(chunkPos.x, chunkPos.z); } default boolean contains(int x, int z) { return this.contains(x, z, true); } boolean contains(int x, int z, boolean includeOuterChunksAdjacentToViewBorder); void forEach(Consumer action); default boolean isInViewDistance(int x, int z) { return this.contains(x, z, false); } static boolean isInViewDistance(int centerX, int centerZ, int viewDistance, int x, int z) { return isWithinDistance(centerX, centerZ, viewDistance, x, z, false); } /** * Check if a chunk {@code (x,z)} is within a {@code viewDistance} which is centered on {@code (centerX, centerZ)} */ static boolean isWithinDistance(int centerX, int centerZ, int viewDistance, int x, int z, boolean includeOuterChunksAdjacentToViewBorder) { int i = includeOuterChunksAdjacentToViewBorder ? 2 : 1; long l = Math.max(0, Math.abs(x - centerX) - i); long m = Math.max(0, Math.abs(z - centerZ) - i); long n = l * l + m * m; int j = viewDistance * viewDistance; return n < j; } public record Positioned(ChunkPos center, int viewDistance) implements ChunkTrackingView { int minX() { return this.center.x - this.viewDistance - 1; } int minZ() { return this.center.z - this.viewDistance - 1; } int maxX() { return this.center.x + this.viewDistance + 1; } int maxZ() { return this.center.z + this.viewDistance + 1; } /** * Determines if another {@link ChunkTrackingView}'s bounds intersects with its own */ @VisibleForTesting protected boolean squareIntersects(ChunkTrackingView.Positioned other) { return this.minX() <= other.maxX() && this.maxX() >= other.minX() && this.minZ() <= other.maxZ() && this.maxZ() >= other.minZ(); } @Override public boolean contains(int x, int z, boolean includeOuterChunksAdjacentToViewBorder) { return ChunkTrackingView.isWithinDistance(this.center.x, this.center.z, this.viewDistance, x, z, includeOuterChunksAdjacentToViewBorder); } @Override public void forEach(Consumer action) { for (int i = this.minX(); i <= this.maxX(); i++) { for (int j = this.minZ(); j <= this.maxZ(); j++) { if (this.contains(i, j)) { action.accept(new ChunkPos(i, j)); } } } } } }