Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Math and primitives

Verified against Minecraft 26.3 · Reference · The coordinate spaces, geometry and randomness every system page assumes — looked up, not watched.

Every system in the game speaks in a handful of value types: an integer block position, a chunk column, a 16³ section, a double-precision world position, a direction, a box, a collision shape, a random source. They are the types the codebase reaches for most — BlockPos alone has 1,229 importers, more than any other Minecraft class — and most of what is confusing about “which coordinate is this” is answered by knowing which type a method takes.

The coordinate spaces

Six integer spaces and one double one, and every conversion is a named method. The figure is the spaces from the finest to the coarsest, and the one that adds a dimension, each arrow the arithmetic of one step; the table under it names the method for each arrow and, for all but GlobalPos, the way back, and the three that also pack to a long key are the next section’s.

flowchart TD
    V["Vec3: a double position"] -- "floor" --> B["BlockPos: one block"]
    B -- "shift 2" --> Q["QuartPos: a 4-block cell"]
    Q -- "shift 2" --> S["SectionPos: a 16-block cube"]
    B -- "shift 4" --> S
    S -- "drop y" --> C["ChunkPos: a 16-block column"]
    C -- "shift 5" --> R["region: 32 chunks a side, one file"]
    B -- "plus a dimension" --> G["GlobalPos"]

The coordinate spaces as a ladder of sizes: after the first step, which floors, every step down is a right shift by as many bits as the next space is coarser, except the one that drops the y axis and the one that adds a dimension; the table names the method for each.

spaceunittypeowner / notesconversions
block1 block, intBlockPos (extends Vec3i)immutable unless it is a BlockPos.MutableBlockPos, the loop subclassBlockPos.containing floors a double position; BlockPos.asLong packs to a long and BlockPos.of unpacks one
world position1 block, doubleVec3 (implements Position)entity positions, ray casts, velocitiesVec3.atCenterOf, Vec3.atLowerCornerOf, Vec3.atBottomCenterOf from a Vec3i; Vec3.directionFromRotation from pitch/yaw
chunk column16 blocksChunkPos — a record of x and zpacked to a long, the key of the chunk maps in ChunkMap and the ticket systemChunkPos.containing from a BlockPos; ChunkPos.pack / ChunkPos.unpack; ChunkPos.getMinBlockX, ChunkPos.getWorldPosition
section16³ cubeSectionPos (extends Vec3i)lighting, entity sections, render sectionsSectionPos.of from block/chunk/entity; SectionPos.blockToSectionCoord (shift 4), SectionPos.sectionToBlockCoord, SectionPos.sectionRelative (mask 15); SectionPos.chunk drops y; SectionPos.asLong
quart / biome4 blocksQuartPos (static only)biome samplingQuartPos.fromBlock (shift 2), QuartPos.toBlock, QuartPos.fromSection, QuartPos.toSection
region32 chunksnone — methods on ChunkPosthe .mca file gridChunkPos.getRegionX, ChunkPos.getRegionLocalX, ChunkPos.minFromRegion; ChunkPos.REGION_SIZE
dimension-qualified block—GlobalPos — record of a Level key and a BlockPoscompass targets, beds, a player’s last death locationGlobalPos.of
integer boxblocksBoundingBox for structures; BlockBox is a newer record that nothing yet usesstructure bounds, piece placementBoundingBox.intersects, BoundingBox.encapsulate; BlockBox.aabb, BlockBox.contains
double boxblocksAABB (a class of six public final doubles)entity bounding boxes, block shapes’ boundsAABB.move, AABB.inflate, AABB.intersects, AABB.clip
pitch / yawdegrees, floatVec2look direction, as (xRot, yRot) — except the Rotation NBT tag, which stores it the other way roundDirection.fromYRot, Direction.toYRot
pose rotationdegrees, float ×3RotationsArmorStand poses, and nothing else—
model / render spacefloatJOML Vector3f, Matrix4f, Quaternionf (external)everything under client/rendererVec3.toVector3f; Direction.step, Direction.getRotation; com/mojang/math Axis builds quaternions

Position is the three-double interface Vec3 implements. Vec3i is the int triple with the arithmetic — immutable to every caller that does not hold a BlockPos.MutableBlockPos, which the closer takes up — (Vec3i.offset, Vec3i.relative, Vec3i.distSqr, Vec3i.distManhattan); BlockPos adds the iteration helpers (BlockPos.betweenClosed, BlockPos.withinManhattan, BlockPos.spiralAround), each of which walks a single reused BlockPos.MutableBlockPos rather than allocating. BlockPos.breadthFirstTraversal is the exception in the same class: it is not an iterator at all, and allocates a queue of nodes and a set of visited longs. Cursor3D is a different allocation-free box cursor — the one that also classifies each position as inside, face, edge or corner (Cursor3D.TYPE_INSIDE, Cursor3D.TYPE_FACE, Cursor3D.TYPE_EDGE, Cursor3D.TYPE_CORNER) — and it is used by SectionPos, BlockCollisions and ClientLevel, not by BlockPos.

Colours are a primitive too, and they live in net/minecraft/util: ARGB is where every pack, unpack, lerp, multiply and alpha helper is, with CommonColors for the named constants, ColorRGBA for the codec-friendly value and Brightness for the packed block/sky light pair.

Three long keys

Three long-packings appear everywhere as map keys.

  • BlockPos.asLong — 26 bits X, 26 bits Z, 12 bits Y, high to low. BlockPos.PACKED_HORIZONTAL_LENGTH is derived from 30,000,000, the level’s hard horizontal bound (Level.MAX_LEVEL_SIZE), which is why it is 26 (BlockPos.MAX_HORIZONTAL_COORDINATE is 33,554,431); the remaining BlockPos.PACKED_Y_LENGTH is 12, giving −2048 to 2047. The usable range is narrower: DimensionType reserves a 32-block margin, so DimensionType.MIN_Y is −2032, DimensionType.MAX_Y is 2031 and DimensionType.Y_SIZE is 4064. BlockPos.getFlatIndex masks off the low four Y bits — it snaps a packed position to the bottom of its own 16-block section, which is the skylight walk-up idiom, not a per-column key (the top eight Y bits survive). BlockPos.STREAM_CODEC sends the packed long; Vec3i.STREAM_CODEC sends three varints.
  • SectionPos.asLong — 22 bits X, 22 bits Z, 20 bits Y. A section-relative position packs into a short (SectionPos.sectionRelativePos).
  • ChunkPos.pack — X in the low 32 bits, Z in the high 32. ChunkPos.INVALID_CHUNK_POS is a sentinel; ChunkPos.isValid is bounded by ChunkPyramid.MAX_CHUNK_COORDINATE_VALUE, which lives with the chunk status pyramid because the safety margin is derived from how many neighbours generation reads.

Directions and symmetry

Direction is the six-valued enum in 3D-data order DOWN, UP, NORTH, SOUTH, WEST, EAST (Direction.get3DDataValue); its horizontal subset has its own order starting at south (Direction.get2DDataValue). Direction.Axis (X, Y, Z), Direction.AxisDirection and Direction.Plane (horizontal, vertical) are the nested helpers; CompositeDirection.Direction8 the compass points; FrontAndTop the twelve jigsaw and crafter orientations; AxisCycle the axis permutation VoxelShape lookups use.

Block rotation is Rotation and Mirror (world/level/block), each of which maps onto OctahedralGroup (com/mojang/math) — the 48-element symmetry group of a cube, each element a permutation plus three inversion flags. That group is not decoration: Shapes.rotate, Shapes.rotateAll, Shapes.rotateHorizontal and Shapes.rotateAttachFace are how a block declares one shape and is handed the rest — three more from Shapes.rotateHorizontal, five from Shapes.rotateAll, and eleven from Shapes.rotateAttachFace, which is Shapes.rotateHorizontal run once per AttachFace. Transformation wraps a JOML matrix and lazily decomposes it into translation, left rotation, scale and right rotation for model JSON and a display entity’s transformation.

There are two things called Axis in scope: Direction.Axis and the quaternion factory in com/mojang/math. They are unrelated, and a grep finds a third in the save-migration tree, which this book does not cover.

Shapes and collision

A VoxelShape is a set of boxes on a per-axis coordinate grid, backed by a DiscreteVoxelShape bit grid (BitSetDiscreteVoxelShape). Shapes is the factory and algebra: Shapes.block, Shapes.empty, Shapes.box, Shapes.or, Shapes.join with a BooleanOp, Shapes.joinIsNotEmpty, Shapes.collide, Shapes.blockOccludes, with Shapes.EPSILON (1.0E-7) the tolerance the shape algebra’s own comparisons use and Shapes.BIG_EPSILON a coarser 1.0E-6; a collision context’s is above test uses a looser 1.0E-5. Implementations differ by how the grid is stored — CubeVoxelShape (even divisions), ArrayVoxelShape (explicit coordinate lists), SliceShape (a one-cell-thick view, used for face culling and occlusion) — and Shapes.joinUnoptimized picks an IndexMerger strategy per axis, building a CubeVoxelShape only when all three merge evenly; Shapes.join is that followed by VoxelShape.optimize.

Context-free collision and face-sturdiness queries are cheap because they are mostly not computed (the outline, interaction and support shapes are asked of the block every time): BlockBehaviour.BlockStateBase.initCache builds a BlockBehaviour.BlockStateBase.Cache per block state holding the collision shape, whether the collision shape reaches outside its cell, whether the collision shape is a full block, and a per-face sturdiness array — but only for a block whose shape is not dynamic, and the occlusion shape is not in it. That one is a field on BlockBehaviour.BlockStateBase itself, built whether the cache is or not. Without a cache a block answers those queries live, and says it has a large collision shape whatever it has; and the query an entity’s movement makes, with its CollisionContext, is never cached — it asks the block every time.

CollisionContext is what a shape query knows about who is asking: CollisionContext.of an entity (EntityCollisionContext — descending, bottom Y, held item, whether fluids collide), CollisionContext.empty, CollisionContext.placementContext — all three of which are EntityCollisionContexts, as is the MinecartCollisionContext CollisionContext.of returns for a minecart under the experimental movement flag. The one case that is neither is PositionCollisionContext, from CollisionContext.positionContext. Ray casts return a HitResult: BlockHitResult (position, face, inside, world-border) or EntityHitResult.

Two random families, a saved table, and a mixer

RandomSource (net/minecraft/util) is the interface. Most implementations live in world/level/levelgen, and the legacy family shares BitRandomSource, which defines RandomSource.nextInt and friends on top of a raw bit generator; XoroshiroRandomSource implements RandomSource directly, and RandomSequences keeps one more in world/. Two families coexist in one process:

  • Legacy LCG — LegacyRandomSource (the java.util.Random algorithm), SingleThreadedRandomSource (same, no atomics), ThreadSafeLegacyRandomSource. RandomSource.create returns a LegacyRandomSource with a uniquified seed; this is Level.random (inherited unchanged by ServerLevel and ClientLevel), Entity.random, ParticleEngine.random. RandomSource.createThreadLocalInstance returns a SingleThreadedRandomSource and is what ClientLevel.animateTick uses for block animation, and LevelRenderer for the block-destroy overlay.
  • Xoroshiro — XoroshiroRandomSource (128-bit state via RandomSupport.Seed128bit), the one a noise settings file gets unless its required legacy_random_source flag asks for legacy: NoiseGeneratorSettings.getRandomSource returns WorldgenRandom.Algorithm.XOROSHIRO unless the settings opt into legacy. Four of the seven shipped noise settings do opt in — nether, end, caves and floating_islands — and the overworld is not one of them, nor are amplified and large_biomes. So an ordinary world runs both families at once: its overworld’s noise is Xoroshiro and the noise of the two dimensions you walk into through a portal is legacy, while every dimension carves, and chooses where its structures go, with a legacy source, and decorates with a Xoroshiro one. RandomState forks the chosen algorithm positionally (PositionalRandomFactory.at, PositionalRandomFactory.fromHashOf) for the named noise consumers — the aquifer and the ore placer each get their own deterministic stream from the seed and position.

WorldgenRandom wraps any delegate and adds the seeding conventions — WorldgenRandom.setDecorationSeed, WorldgenRandom.setFeatureSeed, WorldgenRandom.setLargeFeatureWithSalt, WorldgenRandom.seedSlimeChunk — that make a structure land in the same place for the same seed. Features go through those, not through RandomState.

There is a third randomness path, on the Xoroshiro family: RandomSequence and RandomSequences, a saved, Identifier-keyed table of XoroshiroRandomSource streams derived from the world seed, which is what makes a loot table and /random reproducible across sessions. And two that are not a RandomSource at all: LinearCongruentialGenerator, the bare mixer BiomeManager uses for biome fuzzing, and Level.randValue, the integer Level.getBlockRandomPos steps for random-tick positions.

RandomSource.nextGaussian is produced by MarsagliaPolarGaussian, which caches a spare value — which is why reseeding a source resets it, in every implementation but the deprecated ThreadSafeLegacyRandomSource and WorldgenRandom, whose WorldgenRandom.setSeed reseeds only its delegate.

Mth and Util

Mth is the maths grab-bag (703 importers): Mth.floor, Mth.clamp, Mth.lerp, Mth.wrapDegrees, Mth.rotLerp, Mth.smallestEncompassingPowerOfTwo, Mth.log2, Mth.positiveModulo, Mth.hsvToRgb. Mth.sin and Mth.cos are lookups in a 65,536-entry table (Mth.cos is the same table with a quarter-turn phase shift), and the table is filled from the JDK’s ordinary sine rather than its strict one — so the platform-dependent step, if you are chasing animation determinism, is the table’s construction and not the lookup. Util (in net/minecraft/util, 457 importers) is where the executors live — Util.backgroundExecutor, Util.ioPool, Util.nonCriticalIoPool — along with time sources and collection helpers; Unit is the single-valued “void” type codecs and futures use. The other numeric odds and ends worth knowing by name are CubicSpline (terrain shaping), InclusiveRange, and BitStorage, the packed-integer array underneath every palette.

What trips people up

ChunkPos is a record with no asLong. The names are ChunkPos.pack and ChunkPos.unpack; construction from a block is ChunkPos.containing; the components are accessed as x() and z().

Vec3i.toMutable returns a JOML Vector3i, not a BlockPos.MutableBlockPos; the mutable block position comes from BlockPos.mutable or its constructor, and its BlockPos.MutableBlockPos.set / BlockPos.MutableBlockPos.move are the loop idiom.

A BlockPos may be mutable. Vec3i keeps protected setters that BlockPos.MutableBlockPos, a subclass of BlockPos, makes public; every other subclass treats them as final, so a BlockPos reference can hold a position that moves. BlockPos.immutable is the copy to call before storing a mutable one.

Level.random crashes when two threads draw at once. LegacyRandomSource holds an atomic seed not for safety but as a detector: a draw or a reseed whose compare-and-set loses to another thread’s write raises a ThreadingDetector exception, so concurrent use is caught when two uses collide and not otherwise. The genuinely safe variant, ThreadSafeLegacyRandomSource, and RandomSource.createThreadSafe are both deprecated. Touching a level’s random from a worker is loud only when it collides.

Tick randomness and worldgen randomness do not divide by family. The LCG drives every Level and Entity, the Nether’s and the End’s noise, and every dimension’s carvers and structure placement; the saved RandomSequences behind loot and /random are Xoroshiro, and so are the overworld’s noise and every dimension’s features. PositionalRandomFactory.parityConfigString is implemented by both families, so the parity dumps cover whichever one a dimension’s noise settings choose.

BlockBox is declared and unused. It is a tidy BlockPos-pair record in net/minecraft/core, and in 26.3 nothing calls it; structure bounds are still BoundingBox in world/level/levelgen/structure. Worth knowing before assuming a rename happened.

Direction.getRotation treats UP as identity, and Direction.step returns a fresh JOML vector while Direction.getUnitVec3f returns the read- only shared one.

SectionPos has SectionPos.x as both an instance and a static method, instance and static, same name.

Not everything with Block in the name is in net/minecraft/core. BlockBox, BlockMath and Cursor3D are; BlockUtil is in net/minecraft/util (naming drift). BlockMath is model-rotation plumbing, not coordinates.

Where to look

Vec3i · BlockPos · ChunkPos · SectionPos · QuartPos · GlobalPos · Cursor3D · Vec3 · AABB · BoundingBox · Direction · Rotation · OctahedralGroup · VoxelShape · Shapes · CollisionContext · HitResult · ARGB · Mth · RandomSource · BitRandomSource · WorldgenRandom · PositionalRandomFactory · RandomSequences · Util


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.