Development
How to build, test, and extend NV2 Engine.
Prerequisites
- Rust toolchain (stable is enough for the game)
- GPU with Vulkan / Metal / DX12 / GL support (wgpu backends)
- For the content tools: .NET 8 SDK (
Bridge/Tools) and Python 3 + Pillow (generate_textures.py)
Build & run
cd Core
cargo run --release # optimized build
cargo run # dev build (third-party crates still at opt-level 3)
cargo check --release # fast compile check
cargo test # unit tests (commands, crafting, AI)
VS Code
.vscode/tasks.json already configures a task to run the engine executable
from the workspace. A custom Vulkan layer (VkLayer_NV20.json /
VkLayer_NV20.dll) can be attached from the launch configuration.
Testing
Unit tests exist in key modules:
| Module | Tests cover |
|---|---|
crafting.rs | Shaped recipe matching with offset alignment, shapeless multiset matching |
commands.rs | Command execution flows (locate forest/highland, /ai_* round-trips and validation) |
world/ai_generator.rs | Forward pass validity, training loss decrease, NaN hardening, model bundles, datasets, preferences |
world/memplp.rs | MLP forward/train, deterministic init, legacy migration, NaN survival |
AI commands
| Command | Effect |
|---|---|
/ai_export <path> [author] | Export the live model as a portable community bundle |
/ai_import <path> | Import a shared model bundle and persist it |
/ai_dataset <path> [epochs] | Train the vegetation head on a JSON dataset |
/ai_stats | Show live model stats + player preferences |
Extending: add a block type
- Add the variant to
BlockTypeenum (world/block.rs). - Register it in
BLOCK_REGISTRY:(id, "name", "texture_name"). - Add hardness in the
hardness()match. - Map the texture in the texture registry match.
- Add a display name in
name(). - (Optional) Teach the AI to place it in
place_ai_vegetation().
Extending: add a recipe
Shaped (in crafting.rs default_recipes()):
recipes.register_shaped(ShapedRecipe {
pattern: vec![Some(BlockType::Planks), Some(BlockType::Planks)],
output: stack_of(BlockType::Stick, 4),
// ...
});
Shapeless:
recipes.register_shapeless(ShapelessRecipe {
ingredients: vec![/* ... */],
output: stack_of(BlockType::FlintPickaxe, 1),
});
Recipes can also be loaded from JSON through assets.rs utilities.
Extending: disable the AI (testing)
In vegetation.rs populate_world_trees_for_chunk():
pub fn populate_world_trees_for_chunk(...) {
self.place_trees(world, generator, cx, cz);
// self.place_ai_vegetation(world, generator, cx, cz); // comment out
}
Content pipeline
| Tool | Purpose |
|---|---|
Bridge/Tools/Slicer | .NET 8 atlas slicer — scans atlas PNGs, extracts block textures via predefined or analyzed tile rectangles |
generate_textures.py | Pillow texture transforms — rotate, flip, grayscale, invert, darken, brighten |
Assets/Blocks/ | Runtime texture discovery for the dynamic atlas |
Save format
Worlds persist to saves/world.json (next to the executable):
{
"seed": "...",
"chunks": { "...": "flattened block data" },
"water_meta": "...",
"crafters": { "...": "NVCrafterState" }
}
Settings persist to settings.json (pretty JSON via serde).
Migration / backward compatibility
- Old worlds load fine — AI only affects newly generated chunks.
- Public APIs are stable —
ai_systemispubbut non-blocking to existing systems; new block types don't conflict.