The version that broke everything we built, and forced us to rebuild it better.
Minecraft Infdev is where the port stopped being incremental. Every previous version — from rd-132211 through c0.30_01c — was a variation on the same architecture: a fixed-size byte array, a fixed set of render chunks, and a game loop that fit in a few hundred lines. Infdev threw all of that away and rebuilt from scratch. So did we.
The Java source decompiled to 341 files and 38,980 lines, heavily obfuscated with single-letter class names. Our Rust port weighs in at 6,372 lines across 31 files (not counting tests), plus a renderer shader. We went from 3,065 lines of Rust to over 6,300 — more than doubling the codebase. Every major module was either created new or substantially rewritten.
The Chunk System
The core architectural change is replacing Level (a fixed 256x256x64 world) with World (a HashMap<(i32, i32), Chunk> of 16x128x16 chunks).
The Chunk struct stores 32,768 blocks in a flat Vec<u8>, plus a heightmap for lighting. World coordinates map to chunk coordinates via div_euclid(16) and local coordinates via rem_euclid(16). This handles negative coordinates correctly — a problem that has tripped up many Minecraft clones that use regular integer division.
pub fn get_block(&self, x: i32, y: i32, z: i32) -> u8 {
if y < 0 || y >= 128 { return 0; }
let (cx, cz) = Self::chunk_key(x, z);
self.chunks.get(&(cx, cz)).map_or(0, |c| {
c.get_block(x.rem_euclid(16), y, z.rem_euclid(16))
})
}
The ensure_chunk method lazily creates chunks when they’re first accessed, and the worldgen module populates them with terrain on first generation.
Terrain Generation
Our terrain generator uses the noise crate’s Perlin noise implementation with three octaves at different scales:
- Large-scale terrain shape (scale 0.005, amplitude 32 blocks)
- Medium detail (scale 0.01, amplitude 16 blocks)
- Fine variation (scale 0.05, amplitude 4 blocks)
The sum, centered at sea level (Y=64), determines the terrain height at each column. Below the surface: stone, with a dirt layer on top. At the surface: grass above sea level, sand at or below. Water fills any air below sea level.
Cave generation uses a dual-Perlin approach: two 3D noise fields sampled at each block position. Where both fields exceed a threshold, the block is carved to air. This creates interconnected cave networks without the complexity of Java’s worm-carving algorithm.
Ore veins scatter through stone: coal everywhere (20 attempts per chunk, veins of 8), iron below Y=64 (20 attempts, veins of 6), gold below Y=32 (2 attempts, veins of 5). Each vein randomly spreads from a seed point, replacing stone with ore.
66 Block Types
The tile registry grew from 20 IDs to 66, each with texture mappings, solidity flags, and light-blocking properties. New blocks include:
- 16 wool colors (IDs 21-36): Simple solid blocks with colored textures
- Flowers and mushrooms (37-40): X-cross rendered, non-solid, like the existing bush
- Metal blocks (41-42): Per-face textures (top, side, bottom all different)
- Torch (50): Not solid, doesn’t block light, rendered as crossed quads
- Chest (54), Crafting table (58), Furnace (61-62): Per-face textured blocks
- Diamond ore (56): The rarest underground block
- Ladder (65) and Rail (66): Flat single-face blocks
Each block needed appropriate entries in get_tile_texture, tile_is_solid, tile_blocks_light, and the mesh builder. Helper functions like tile_is_torch() and tile_is_flat() guide the renderer to use the correct geometry.
Items and Crafting
Three entirely new modules:
item.rs defines ItemStack (id, count, damage) and 70+ item ID constants. Tool tiers (Wood through Diamond) have speed multipliers and durability values matching the Java originals.
inventory.rs manages 36 inventory slots (9 hotbar + 27 main) plus 4 armor slots. Stack merging follows Minecraft rules: same item ID, up to 64 per stack.
crafting.rs implements shaped 3×3 recipe matching with 31 registered recipes. The matcher handles offset — a 2×3 pickaxe recipe can be placed anywhere in the grid. The recipe system covers planks, sticks, torches, crafting tables, furnaces, chests, and all 25 tool combinations (5 tiers times 5 types).
The Mob System
mob.rs introduces living entities with health (20 max), air supply (300 ticks for drowning), fall damage (distance – 3 blocks), and hurt/death animations.
The mobs/ module contains six mob types, each wrapping Mob with custom stats: Zombie (melee, 20 HP), Skeleton (ranged, 20 HP), Creeper (explosive, 20 HP), Spider (jump attack, 16 HP), Pig (passive, 10 HP), and Sheep (passive with wool color, 8 HP).
The AI system defines BasicAI (random wandering with idle periods) and AttackAI (chase player within 16 blocks, melee within 2).
NBT Serialization
nbt.rs implements the full Named Binary Tag format: 10 tag types, big-endian encoding, GZip compression. The module handles reading and writing both in-memory and to files.
World save/load uses NBT for both level.dat (seed, spawn, time) and per-chunk files (c.<cx_base36>.<cz_base36>.dat). Base-36 filenames match the Java convention.
Rendering Overhaul
The renderer grew from 1,201 lines to 1,904. Key changes:
Dynamic chunk management: A HashMap<(i32,i32,i32), ChunkRender> replaces the fixed Vec<ChunkRender>. Chunks load within render distance (8 chunks) and unload beyond it. Each 16x128x16 chunk splits into 8 vertical sections (16x16x16) for independent frustum culling.
Day/night cycle: A sky_brightness uniform modulates block lighting in the shader. The sky color lerps from blue (day) to dark blue (night). Clear color darkens to match. Stars render as 200 small quads when brightness drops below 0.5.
New block geometry: The mesh builder handles torches (crossed vertical quads), ladders (flat wall faces), rails (flat ground faces), and all new bush-type blocks.
HUD: Hotbar with 9 slots at bottom center, selected slot highlighted. Health hearts above the hotbar, rendered as colored quads.

Sea-level water and infinite terrain stretching to the horizon
Tile Entities
tile_entity.rs adds persistent block state for Furnaces (input/fuel/output slots, burn/cook timers), Chests (27 item slots), and Signs (4 text lines). Chunks store tile entities in a HashMap<(i32,i32,i32), TileEntity>.
Player Upgrades
The player gained health (20), inventory, air supply, fall distance tracking, and a tick_world method that handles physics against the infinite world’s collision system.
The Integration Challenge
Wiring everything together in main.rs required careful navigation. The old Level-based code path is preserved for backward compatibility (old tests still reference it). The new World-based path runs the actual game.
Chunk generation happens lazily as the player moves: a 9×9 chunk radius around the player is checked each tick, and any ungenerated chunks get populated with terrain. This means the world truly is infinite — walk in any direction and new terrain appears.
By the Numbers
| Metric | Classic (c0.30_01c) | Infdev (inf-20100618) | Change |
|---|---|---|---|
| Java files | 181 | 341 | +88% |
| Java lines | 16,561 | 38,980 | +135% |
| Rust files | 14 | 31 | +121% |
| Rust lines | 3,065 | 6,372 | +108% |
| Block types | ~50 | 66 | +32% |
| Item types | 0 | 73 | New |
| Parity tests | 106 | 176 | +66% |
The Hardest Bug
The most subtle issue was negative coordinate handling. In Java, -1 / 16 equals 0 (truncation toward zero). In Rust, -1_i32 / 16 also equals 0. But we need floor division: the block at x=-1 belongs to chunk -1, not chunk 0. Rust provides div_euclid for exactly this case: -1_i32.div_euclid(16) correctly returns -1, while -1_i32.rem_euclid(16) returns 15.
Getting this wrong produces spectacular failures: blocks placed at negative coordinates appear in the wrong chunk, terrain generates with seams at the origin, and collision detection breaks in the negative quadrants. One function call — div_euclid instead of / — and everything works. Miss it and you spend hours debugging phantom walls.
What’s Deferred
Even with the “full send,” some features are deferred to avoid scope explosion:
- Mob spawning logic — Mob types exist but aren’t spawned naturally in-world yet
- Crafting/furnace GUI — The systems work but the interactive screens aren’t rendered
- Block breaking animation — Instant break rather than progressive
- Redstone — Wire block registered but no signal propagation
- Doors/signs — Block IDs registered but no open/close or text interaction
- Sound — PaulsCode/OpenAL integration remains out of scope
What’s Next
Ten versions done. 176 tests. The architectural foundation for infinite worlds is in place. From here, every future version — Alpha, Beta, Release — builds on chunks, items, and entities. The hard part is behind us. The fun part starts now.
The transition from fixed-size levels to infinite chunks is the kind of architectural leap that separates toy projects from real ones. Every assumption baked into the old code — that blocks have valid indices, that the world has edges, that you can iterate over all blocks — had to be questioned and often discarded. What remains is a system that can grow without bounds, generating new terrain as fast as the player can walk. That is the promise Infdev made, and that is what we have delivered.
Check out the full source at github.com/BangRocket/rustcraft.
