A Minecraft-like voxel game written in C using OpenGL, featuring procedural world generation, multiplayer networking, level-of-detail rendering, and a multithreaded architecture.
- Procedural terrain generation with Perlin noise
- Multiple biomes with distinct characteristics (configurable via
res/biomes.json) - Dynamic chunk loading and management with configurable render distance
- Procedural tree generation with JSON-based definitions
- Water physics with water level simulation and fluid flow mechanics
- Extensive block types with JSON-based configuration (
res/blocks.json) - Support for opaque, transparent, liquid, and foliage block types
- Custom block models using Blockbench format
- Block placement and destruction with raycast-based selection
- Oriented blocks (blocks that can face different directions)
- Texture atlas support for efficient block rendering
- Advanced water rendering with reflections and refraction
- Dynamic shadow mapping with softened shadows (PCF sampling)
- Screen-space reflections for water surfaces
- Liquid renderer with caustic effects
- Foliage renderer with alpha blending
- Skybox and sun rendering
- UI system with configurable FPS counter and hotbar display
- Wireframe mode for debugging
- Dynamic LOD scaling based on player distance from each chunk
- Per-LOD chunk mesh caching — distant chunks use coarser geometry, reducing vertex count and draw calls
- Configurable via
lod_scaling_constantandmax_lod_block_sizeinres/settings.json
- Client-server architecture over TCP
- Chunk synchronization between server and all connected clients
- RLE compression for chunk data (~100x reduction on typical terrain)
- Broadcast system for real-time world updates to multiple clients
- Headless server mode — run without graphics using the
--serverflag - Thread-local socket pool for zero-lock socket access per thread
- Configurable host, port, and world storage directory in
res/settings.json
- Worker pool with configurable thread count for parallel chunk mesh generation
- Mesh batching for efficient GPU utilization
- Chunk-based world organization
- Configurable graphics settings (mipmapping, atlas size, tick rate)
malloc-craft uses a multithreaded architecture to keep gameplay smooth while handling chunk loading, mesh generation, and networked updates concurrently.
Client-side threads:
- Main/Render Thread: Handles rendering, input, and overall game loop. Updates game state and coordinates mesh updates.
- Worker Pool (N threads): Parallel chunk mesh generation. The number of worker threads is configurable via
worker_threadsinres/settings.json(default: 8). Seesrc/mesh/core/worker_pool.c. - Chunk Mesh Updater Thread: Dispatches dirty chunks to the worker pool and collects results, separating blocks into opaque, transparent, liquid, and foliage layers. See
src/mesh/generation/chunk_mesh.c(start_chunk_mesh_updater). - World Mesh Updater Thread: Periodically rebuilds the combined world mesh from individual chunk meshes for efficient rendering. See
src/mesh/generation/world_mesh.c(start_world_mesh_updater).
Server-side threads:
- Listener Thread: Accepts incoming client connections.
- Per-Client Receive Thread: One thread per connected client, reads incoming messages.
- Broadcast Thread: Distributes world updates to all connected clients from a shared queue.
Shared data such as mesh buffers, game state, and camera position are protected using mutexes to ensure thread safety and prevent race conditions. Key synchronization:
lock_mesh()/unlock_mesh()– Protect mesh data during updates- Thread-local socket pool (
pthread_key_t) – Per-thread sockets with no locking needed - Game data is shared via the
game_datastructure with proper locking mechanisms
- Main thread updates player camera and detects chunk changes
- Chunk mesh updater dispatches dirty chunks to the worker pool
- Worker threads generate per-chunk meshes in parallel (opaque, transparent, liquid, foliage, LOD variants)
- World mesh updater combines completed chunk meshes into batched world mesh packets
- Main render thread retrieves updated meshes and renders to screen
- On multiplayer servers, chunk updates are also broadcast to all connected clients via the broadcast queue
Meshes are organized into separate buffers by rendering layer:
- Opaque: Solid blocks (stone, dirt, etc.)
- Transparent: Blocks with alpha (glass, etc.)
- Liquid: Water blocks with flow simulation
- Foliage: Grass and plant-like blocks with special rendering
- Custom Models: Blockbench-based decorative models
When adding new threaded features:
- Always use proper mutex locking to protect shared data
- Watch for potential deadlocks and race conditions
- Test with multiple chunks loading/unloading simultaneously
- Use frame time monitoring to ensure threading isn't causing stalls
- Consider thread pool patterns for many-to-one operations
src/mesh/generation/chunk_mesh.c– Chunk mesh generationsrc/mesh/generation/world_mesh.c– World mesh aggregationsrc/mesh/core/mesh.c– Mesh data structure and LOD cachingsrc/mesh/core/worker_pool.c– Worker thread poolinclude/game_data.h– Shared game state
malloc-craft supports a dedicated server mode that allows multiple clients to connect and share the same world.
./malloc-craft --serverThe server runs headlessly (no window or graphics context). Clients connect using the host and port configured in res/settings.json.
- The server maintains an in-memory chunk cache (up to 1024 chunks) with disk eviction for overflow
- Chunk data is RLE-compressed before transmission: a typical chunk compresses from ~12KB to ~120 bytes
- The server relays compressed chunks directly to clients without decompressing (broadcast-without-decompress)
- Each client gets its own receive thread; outgoing updates go through a single broadcast queue
"server": {
"port": 8085,
"host": "192.168.4.141",
"worlds_dir": "./worlds/"
}Chunk packets contain: x (4B) | z (4B) | packet_count (4B) followed by runs of block_data (3B) | run_length (4B).
For an in-depth look at the networking internals, see MULTIPLAYER_DEEP_DIVE.md.
malloc-craft is highly configurable through JSON files in the res/ directory:
- Resolution & Display: Window dimensions, fullscreen mode, V-sync, FOV
- Chunk Management: Per-frame chunk load rate, cache size, render distance, worker thread count
- LOD:
lod_scaling_constant,max_lod_block_size— control when and how aggressively LOD kicks in - Graphics: Wireframe mode, atlas size, mipmapping, tick rate
- Textures: Atlas, bump map, skybox, and caustic texture paths
- Server: Host, port, and world storage directory
- Blocks (
res/blocks.json): Block types, textures, properties (transparent, liquid, foliage, oriented) - Biomes (
res/biomes.json): Biome types with surface/subsurface/underground/underwater block types and foliage distribution - Trees (
res/trees.json): Procedural tree generation rules and variations - UI (
res/ui.json): HUD layout including FPS counter position and hotbar styling - Player (
res/player.json): Default player spawn state
For detailed settings information, see the JSON files directly.
src/
├── render/ # Graphics rendering subsystem
│ ├── core/ # Shader, texture, window management
│ ├── geometry/ # VAO/VBO (vertex buffer management)
│ ├── effects/ # Shadow maps, reflections, frame buffers
│ ├── world/ # Block, liquid, and foliage renderers
│ ├── entities/ # Custom model and outline rendering
│ ├── environment/ # Skybox and sun rendering
│ └── ui/ # UI renderer for HUD elements
├── mesh/ # Mesh generation and management
│ ├── core/ # Mesh data structures, LOD caching, worker pool
│ ├── generation/ # Chunk mesh and world mesh builders
│ └── geometry/ # Blockbench model loading
├── player/ # Player logic and input handling
│ ├── core/ # Camera and player state
│ ├── input/ # Keyboard/mouse input processing
│ └── serialization/ # Player data persistence
├── world/ # World simulation
│ ├── core/ # Chunks, blocks, world state
│ ├── generation/ # Biome, terrain, and tree generation
│ └── physics/ # Water physics and fluid flow
├── server/ # Multiplayer server
│ ├── server.c # Server entry point and socket setup
│ ├── client.c # Client connection management, thread-local socket pool
│ ├── threads/ # Listener, broadcast, and per-client receive threads
│ ├── world/ # Server-side world state and chunk disk I/O
│ └── compression/ # RLE chunk compression/decompression
├── util/ # Utility functions and settings
└── glad/ # OpenGL loader (auto-generated)
include/
├── cglm/ # Math library headers
├── GLFW/ # Window/input library headers
├── stb/ # Image loading headers
├── cerialize/ # Custom serialization utilities
├── game_data.h # Core game state structure
├── hashmap.h # Hash map implementation
├── block_models.h # Blockbench model definitions
└── util.h # Utility function declarations
lib/
├── cglm/ # Math library for graphics (submodule)
├── GLFW/ # Window/input handling (submodule)
└── glad/ # OpenGL loader (included)
res/
├── shaders/ # GLSL vertex and fragment shaders
│ ├── world/ # world, liquid, foliage, blockbench shaders
│ ├── effects/ # shadow and reflection shaders
│ ├── environment/ # skybox and sun shaders
│ └── ui/ # outline and HUD shaders
├── textures/ # Game textures
├── blocks.json # Block type definitions
├── biomes.json # Biome configurations
├── trees.json # Tree generation rules
├── ui.json # UI layout and styling
├── settings.json # Engine configuration
├── player.json # Default player state
└── blockbench/ # Custom block models
- CMake (version 3.10 or higher)
- C Compiler with C11 support (gcc or clang)
- OpenGL 3.3+ compatible graphics hardware and drivers
- POSIX Threads (pthread) support for multithreading
git clone --recurse-submodules https://github.com/MankowskiNick/malloc-craft.git
cd malloc-craftmkdir -p build && cd build
cmake ..
cmake --build .# Single-player
./malloc-craft
# Dedicated server (headless)
./malloc-craft --server- Linux: Fully supported (developed and tested on Linux/macOS)
- macOS: Fully supported (developed and tested on Linux/macOS)
- Windows: Unsupported
| Key | Action |
|---|---|
| WASD | Move |
| Space | Jump |
| Left Shift | Crouch |
| Double-tap W | Sprint |
| Mouse | Look around |
| Left Click | Destroy block |
| Right Click | Place block |
| 0–9 | Select from hotbar |
| V | Toggle no-clip / fly mode |
| F1 | Toggle FPS counter |
| F11 | Toggle fullscreen |
| ESC | Exit |
- GLFW – Window and input management
- cglm – Graphics math library
- stb – Image loading (stb_image)
- glad – OpenGL loading library
Chunks: The world is divided into chunks (16×256×16 blocks). Each chunk maintains its own mesh data per LOD level and can be loaded/unloaded independently.
Level of Detail: Each chunk stores multiple cached meshes at different LOD levels. The active LOD is selected at render time based on the chunk's distance from the player, reducing geometry for far-away chunks automatically.
Meshes: Three levels of mesh organization:
- Individual chunk meshes (per-chunk vertex data, one per LOD level)
- World mesh packets (grouped chunks for efficient rendering)
- Stratified rendering layers (opaque, transparent, liquid, foliage)
Game Data: All shared state is managed through the game_data structure, including:
- Current camera position and chunk coordinates
- All loaded chunk meshes (with LOD cache)
- World mesh packets
- Player reference
- Frame timing and statistics
- New Block Types: Add JSON definitions to
res/blocks.jsonand corresponding textures to the atlas - New Shaders: Add
.vertand.fragfiles to the appropriateres/shaders/subdirectory, then reference in the relevant renderer - New Biome: Add to
res/biomes.jsonwith foliage and block types - Custom Models: Create in Blockbench, export JSON, add to
res/blockbench/, and reference in block definitions - New Renderers: Create new renderer module in
src/render/, implement shader compilation and rendering logic - Multiplayer Messages: Add new message types in
src/server/, handle on both the listen and broadcast threads