Porting c0.30_01c to Rust: Selective Fidelity at Scale
In which the codebase triples, nine blocks are added, and almost everything else is deferred.
This is the largest version jump so far. The Java source went from 44 files to 181, from 4,700 lines to 16,500. Three entirely new subsystems appeared — sound, multiplayer, mobs — each one larger than the entire game was six months ago. Porting everything would be a months-long project. So we did not port everything.
The principle: port what affects the single-player creative experience. Blocks, rendering, terrain generation, player interaction. Defer what requires infrastructure that does not yet exist in the Rust codebase: networking, audio, mob AI, GUI screens. The deferred features are tracked explicitly. Nothing is silently skipped.
Nine New Tile IDs
The tile registry grew from 12 to over 40 block types in the Java source. We added nine that are essential to the Classic creative experience:
pub const TILE_SAND: u8 = 12;
pub const TILE_GRAVEL: u8 = 13;
pub const TILE_GOLD_ORE: u8 = 14;
pub const TILE_IRON_ORE: u8 = 15;
pub const TILE_COAL_ORE: u8 = 16;
pub const TILE_LOG: u8 = 17;
pub const TILE_LEAVES: u8 = 18;
pub const TILE_SPONGE: u8 = 19;
pub const TILE_GLASS: u8 = 20;
Each required updates to the tile property functions: tile_is_solid(), tile_blocks_light(), tile_texture_index(), and tile_is_opaque(). Sand, gravel, ores, log, and sponge are standard solid opaque blocks. Leaves and glass required new handling.
The 14 wool colors (IDs 21-34) are deferred. Each is a simple solid opaque block with a unique texture index — no behavioral complexity, just texture mapping. They will be added when the terrain atlas includes the wool textures or when a version requires them for gameplay.
Per-Face Textures: Logs
Log blocks are the first tile with different textures on different faces. The top and bottom show the tree’s cross-section (rings on bark). The four sides show bark texture. Every previous block used a single texture index for all six faces.
The texture lookup function gained a face parameter:
pub fn tile_texture_index(tile_id: u8, face: usize) -> usize {
match tile_id {
TILE_LOG => {
if face == 0 || face == 1 { 21 } // top/bottom: cross-section
else { 20 } // sides: bark
}
_ => tile_texture_index_default(tile_id),
}
}
The chunk mesher already passed face information implicitly through its per-face rendering loops. Making it explicit — passing a face index to the texture lookup — was a small refactor with large future implications. Every future block with per-face textures (grass blocks with green tops and dirt sides, furnaces with a face texture, crafting tables) will use this same mechanism.
Transparency: Glass and Leaves
Glass and leaves are the first blocks that are solid for collision but require special rendering treatment for transparency.
Glass is physically solid (the player collides with it) but visually transparent. The key rendering difference: faces between two glass blocks are not culled. With standard solid-block culling, a wall of glass would be invisible — every shared face suppressed, leaving only the outer edges. By marking glass as non-opaque, the chunk mesher emits all faces, and the glass texture’s transparent pixels let the world behind show through.
Leaves follow the same pattern: solid for collision, non-opaque for rendering. Faces between leaf blocks are drawn, allowing the canopy to have visual depth rather than appearing as a solid green mass.
The tile_is_opaque() function handles this:
pub fn tile_is_opaque(tile_id: u8) -> bool {
match tile_id {
TILE_AIR => false,
TILE_LEAVES => false,
TILE_GLASS => false,
t if tile_is_liquid(t) => false,
_ => tile_is_solid(tile_id),
}
}
The chunk mesher’s face culling logic was updated to use opacity rather than solidity for determining whether to suppress a face. A face is suppressed only when the neighbor is opaque. Glass next to glass: both faces drawn. Glass next to stone: glass face suppressed (stone is opaque, so nothing to see through it), stone face suppressed (standard solid-solid culling). Glass next to air: glass face drawn.
This is a subtle but important architectural distinction. Solidity governs physics (can the player walk through it?). Opacity governs rendering (can you see through it?). Previous versions conflated the two because every solid block was also opaque. Glass and leaves break that assumption. Separating the concepts now prevents a class of rendering bugs in every future version.
Terrain Generation Updates
The terrain generator was updated to place the new block types:
Ores are scattered through stone below a configurable depth. The placement algorithm is simple: iterate through the block array, and for each stone block below a certain Y threshold, roll a random chance to replace it with gold, iron, or coal ore. Coal is the most common, gold the rarest. The distribution is not sophisticated — no vein systems, no biome-dependent placement — but it populates the underground with visual variety.
Trees are placed on the surface after terrain generation. For each surface column with a grass block on top, a random chance triggers tree placement: a column of log blocks (3-5 tall) topped with a sphere of leaf blocks (radius 2-3). Trees do not check for overlap, so dense forests can produce merged canopies. The tree placement is simple but effective — it transforms the landscape from bare hills into forested terrain.
Sand and gravel replace surface dirt in certain areas, determined by noise thresholds. Beach-like strips of sand appear at water edges. Gravel patches appear on hillsides. The placement is noise-driven, creating natural-looking material transitions rather than sharp boundaries.
What Was Deferred
This is the longest deferred list of any version so far. The 3.3x code growth means most of the new code is infrastructure we are not yet ready to port.
Mobs. The entire entity system beyond the player. Mob classes, model rendering, AI pathfinding, spawning logic, animation. This is approximately 30 files and 3,000 lines. Mobs in Classic are primarily visual — they wander and animate but do not interact with the player in gameplay-meaningful ways. The mob system will be ported when survival mode introduces hostile mobs with combat mechanics.
Sound. The Paul Lamb SoundSystem integration, Ogg Vorbis decoding, positional audio, resource downloading. Sound is deeply integrated in the Java source — block interactions, footsteps, ambient audio all trigger sound events. The Rust port would require either binding to OpenAL or using a Rust audio library (rodio, cpal). Deferred until we establish an audio architecture.
Multiplayer. The networking stack: TCP packet protocol, server connection, player synchronization, chat, server browser, authentication. This is the largest single subsystem in c0.30_01c by file count. Multiplayer requires architectural decisions about async I/O, protocol implementation, and server compatibility that are premature for a port focused on single-player parity. Deferred indefinitely.
Gravity blocks. Sand and gravel have block IDs and textures but do not fall. The gravity mechanic requires converting a block into a falling entity, running physics on that entity, and converting it back to a block on landing. This requires the entity system infrastructure that is deferred with mobs. Sand and gravel render and can be placed/broken, but they sit where placed regardless of what is below them.
Sponge water absorption. Sponge blocks exist and render but do not absorb adjacent water. The absorption mechanic requires iterating nearby blocks and removing water within a radius on placement — a block interaction system that does not yet exist in the Rust port.
GUI screens. Pause menu, settings, server browser, chat overlay. The GUI system is self-contained but substantial. Deferred until a version requires GUI interaction for gameplay.
Items and inventory. The block palette (hotbar) exists in simplified form from previous versions. The full item system, inventory screens, and item entities are deferred.
Test Suite: 106 Tests
Nine new tests bring the total from 97 to 106:
New tile properties (4):
– test_new_classic_tile_ids — sand through glass have correct IDs 12-20.
– test_ore_tiles_solid — gold, iron, and coal ore are solid and block light.
– test_glass_transparent — glass is solid but not opaque.
– test_leaves_transparent — leaves are solid but not opaque.
Per-face textures (2):
– test_log_top_texture — log top/bottom faces return cross-section texture index.
– test_log_side_texture — log side faces return bark texture index.
Terrain generation (3):
– test_terrain_has_ores — generated level contains at least one ore block.
– test_terrain_has_trees — generated level contains at least one log block.
– test_terrain_has_sand — generated level contains at least one sand block.
All 97 previous tests continue to pass. The new tests verify tile properties and terrain generation state without requiring GPU context.
Gap Analysis
| Feature | Status | Notes |
|---|---|---|
| Sand (ID 12) | DONE | Block, texture, terrain placement. Gravity DEFERRED. |
| Gravel (ID 13) | DONE | Block, texture, terrain placement. Gravity DEFERRED. |
| Gold ore (ID 14) | DONE | Block, texture, terrain placement. |
| Iron ore (ID 15) | DONE | Block, texture, terrain placement. |
| Coal ore (ID 16) | DONE | Block, texture, terrain placement. |
| Log (ID 17) | DONE | Per-face textures, terrain placement. |
| Leaves (ID 18) | DONE | Transparent rendering, terrain placement. |
| Sponge (ID 19) | DONE | Block and texture. Water absorption DEFERRED. |
| Glass (ID 20) | DONE | Transparent solid rendering. |
| Wool colors (IDs 21-34) | DEFERRED | 14 simple solid blocks, no behavioral complexity. |
| Per-face texture lookup | DONE | Face parameter added to texture function. |
| Opacity vs. solidity | DONE | Separate concepts for physics vs. rendering. |
| Ore terrain generation | DONE | Random placement in stone below depth threshold. |
| Tree terrain generation | DONE | Log columns with leaf spheres on surface. |
| Sand/gravel terrain placement | DONE | Noise-driven surface material variation. |
| Mob system | DEFERRED | Entity hierarchy, models, AI, spawning. |
| Sound system | DEFERRED | Audio library, Vorbis decoding, positional audio. |
| Multiplayer | DEFERRED | Networking protocol, server connection, sync. |
| Gravity blocks | DEFERRED | Sand/gravel falling entity conversion. |
| Sponge absorption | DEFERRED | Water removal within radius. |
| GUI screens | DEFERRED | Pause, settings, server browser, chat. |
| Arrow entities | DEFERRED | Projectile physics, rendering, collision. |
| Items/inventory | DEFERRED | Full item system beyond block palette. |
Nine features done. Eight deferred. Every deferral is a conscious decision tracked against the Java source, not an oversight.
The Challenge of 3x Growth
The hardest part of this port was not any single feature. It was deciding what to port.
Every previous version was small enough to port completely or nearly completely. c0.0.13a_03 deferred three features. c0.30_01c defers eight. The ratio of ported-to-deferred has inverted. We are now porting less than half of each version’s new code, measured by volume.
This is the correct approach. The deferred features are infrastructure — networking, audio, mob AI — that will be ported once, not re-ported for every version. Multiplayer in c0.30_01c and multiplayer in Alpha 1.0 share architectural bones. Porting the networking stack now and then porting it again when the protocol changes in Alpha would be double work. Better to port it once when the protocol stabilizes, and backfill Classic compatibility if needed.
The nine new blocks, the per-face texture system, and the transparency/opacity distinction are the changes that matter for visual and gameplay parity. A player loading c0.30_01c and our Rust port side by side should see the same landscape: forests of log-and-leaf trees, ore-speckled stone underground, sand beaches at the waterline, glass windows in player-built structures. The mobs are missing. The sound is missing. But the world looks right and the blocks work.
That is selective fidelity: port what you can see and touch, defer what you can hear and what moves on its own.
We have 871 versions to go. The codebase tripled and we kept pace. The tile system scales. The chunk mesher handles three transparency modes. The terrain generator plants trees. The deferred list is long but honest.
The game grew up. The port is keeping up.