package net.minecraft.world.level; import com.mojang.serialization.Codec; import io.netty.buffer.ByteBuf; import java.util.Spliterators.AbstractSpliterator; import java.util.function.Consumer; import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.world.level.chunk.status.ChunkPyramid; import net.minecraft.world.level.chunk.status.ChunkStatus; import org.jetbrains.annotations.Nullable; public class ChunkPos { public static final Codec CODEC = Codec.INT_STREAM .comapFlatMap( intStream -> Util.fixedSize(intStream, 2).map(is -> new ChunkPos(is[0], is[1])), chunkPos -> IntStream.of(new int[]{chunkPos.x, chunkPos.z}) ) .stable(); public static final StreamCodec STREAM_CODEC = new StreamCodec() { public ChunkPos decode(ByteBuf byteBuf) { return FriendlyByteBuf.readChunkPos(byteBuf); } public void encode(ByteBuf byteBuf, ChunkPos chunkPos) { FriendlyByteBuf.writeChunkPos(byteBuf, chunkPos); } }; private static final int SAFETY_MARGIN = 1056; /** * Value representing an absent or invalid chunkpos */ public static final long INVALID_CHUNK_POS = asLong(1875066, 1875066); private static final int SAFETY_MARGIN_CHUNKS = (32 + ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL).accumulatedDependencies().size() + 1) * 2; public static final int MAX_COORDINATE_VALUE = SectionPos.blockToSectionCoord(BlockPos.MAX_HORIZONTAL_COORDINATE) - SAFETY_MARGIN_CHUNKS; public static final ChunkPos ZERO = new ChunkPos(0, 0); private static final long COORD_BITS = 32L; private static final long COORD_MASK = 4294967295L; private static final int REGION_BITS = 5; public static final int REGION_SIZE = 32; private static final int REGION_MASK = 31; public static final int REGION_MAX_INDEX = 31; public final int x; public final int z; private static final int HASH_A = 1664525; private static final int HASH_C = 1013904223; private static final int HASH_Z_XOR = -559038737; public ChunkPos(int x, int y) { this.x = x; this.z = y; } public ChunkPos(BlockPos pos) { this.x = SectionPos.blockToSectionCoord(pos.getX()); this.z = SectionPos.blockToSectionCoord(pos.getZ()); } public ChunkPos(long packedPos) { this.x = (int)packedPos; this.z = (int)(packedPos >> 32); } public static ChunkPos minFromRegion(int chunkX, int chunkZ) { return new ChunkPos(chunkX << 5, chunkZ << 5); } public static ChunkPos maxFromRegion(int chunkX, int chunkZ) { return new ChunkPos((chunkX << 5) + 31, (chunkZ << 5) + 31); } public long toLong() { return asLong(this.x, this.z); } /** * Converts the chunk coordinate pair to a long */ public static long asLong(int x, int z) { return x & 4294967295L | (z & 4294967295L) << 32; } public static long asLong(BlockPos pos) { return asLong(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); } public static int getX(long chunkAsLong) { return (int)(chunkAsLong & 4294967295L); } public static int getZ(long chunkAsLong) { return (int)(chunkAsLong >>> 32 & 4294967295L); } public int hashCode() { return hash(this.x, this.z); } public static int hash(int x, int z) { int i = 1664525 * x + 1013904223; int j = 1664525 * (z ^ -559038737) + 1013904223; return i ^ j; } public boolean equals(Object object) { if (this == object) { return true; } else { return !(object instanceof ChunkPos chunkPos) ? false : this.x == chunkPos.x && this.z == chunkPos.z; } } public int getMiddleBlockX() { return this.getBlockX(8); } public int getMiddleBlockZ() { return this.getBlockZ(8); } /** * Get the first world X coordinate that belongs to this Chunk */ public int getMinBlockX() { return SectionPos.sectionToBlockCoord(this.x); } /** * Get the first world Z coordinate that belongs to this Chunk */ public int getMinBlockZ() { return SectionPos.sectionToBlockCoord(this.z); } /** * Get the last world X coordinate that belongs to this Chunk */ public int getMaxBlockX() { return this.getBlockX(15); } /** * Get the last world Z coordinate that belongs to this Chunk */ public int getMaxBlockZ() { return this.getBlockZ(15); } /** * Gets the x-coordinate of the region file containing this chunk. */ public int getRegionX() { return this.x >> 5; } /** * Gets the z-coordinate of the region file containing this chunk. */ public int getRegionZ() { return this.z >> 5; } /** * Gets the x-coordinate of this chunk within the region file that contains it. */ public int getRegionLocalX() { return this.x & 31; } /** * Gets the z-coordinate of this chunk within the region file that contains it. */ public int getRegionLocalZ() { return this.z & 31; } public BlockPos getBlockAt(int xSection, int y, int zSection) { return new BlockPos(this.getBlockX(xSection), y, this.getBlockZ(zSection)); } public int getBlockX(int x) { return SectionPos.sectionToBlockCoord(this.x, x); } public int getBlockZ(int z) { return SectionPos.sectionToBlockCoord(this.z, z); } public BlockPos getMiddleBlockPosition(int y) { return new BlockPos(this.getMiddleBlockX(), y, this.getMiddleBlockZ()); } public String toString() { return "[" + this.x + ", " + this.z + "]"; } public BlockPos getWorldPosition() { return new BlockPos(this.getMinBlockX(), 0, this.getMinBlockZ()); } public int getChessboardDistance(ChunkPos chunkPos) { return this.getChessboardDistance(chunkPos.x, chunkPos.z); } public int getChessboardDistance(int x, int z) { return Math.max(Math.abs(this.x - x), Math.abs(this.z - z)); } public int distanceSquared(ChunkPos chunkPos) { return this.distanceSquared(chunkPos.x, chunkPos.z); } public int distanceSquared(long packedPos) { return this.distanceSquared(getX(packedPos), getZ(packedPos)); } private int distanceSquared(int x, int z) { int i = x - this.x; int j = z - this.z; return i * i + j * j; } public static Stream rangeClosed(ChunkPos center, int radius) { return rangeClosed(new ChunkPos(center.x - radius, center.z - radius), new ChunkPos(center.x + radius, center.z + radius)); } public static Stream rangeClosed(ChunkPos start, ChunkPos end) { int i = Math.abs(start.x - end.x) + 1; int j = Math.abs(start.z - end.z) + 1; final int k = start.x < end.x ? 1 : -1; final int l = start.z < end.z ? 1 : -1; return StreamSupport.stream(new AbstractSpliterator(i * j, 64) { @Nullable private ChunkPos pos; public boolean tryAdvance(Consumer consumer) { if (this.pos == null) { this.pos = start; } else { int ix = this.pos.x; int jx = this.pos.z; if (ix == end.x) { if (jx == end.z) { return false; } this.pos = new ChunkPos(start.x, jx + l); } else { this.pos = new ChunkPos(ix + k, jx); } } consumer.accept(this.pos); return true; } }, false); } }