Skip to content

[Batch 3] Save/load UI + world list screen #382

@MichaelFisher1997

Description

@MichaelFisher1997

Summary

Add in-game UI for loading saved worlds and creating new worlds that persist. This is the player-facing half of the persistence system.

Depends on: #377 (chunk serialization API) — needs SaveManager.loadChunk() to exist

Current Behavior

The SingleplayerScreen has:

  • Seed text input (keyboard typing)
  • RANDOM button for random seed
  • World type selector (cycles through registered generators)
  • CREATE button → creates new WorldScreen with seed → generates fresh world

There is no "load existing world" option. No save directory is scanned.

Target Behavior

SingleplayerScreen changes

  • Split into two sections: "CREATE NEW WORLD" and "LOAD WORLD"
  • "LOAD WORLD" button → navigates to new WorldListScreen

WorldListScreen (new)

  • Scans ~/.config/zigcraft/saves/ directory
  • Lists each world as a row:
    • World name (from directory name or level.dat)
    • Seed (from level.dat)
    • Last played (timestamp from level.dat)
    • Generator type (from level.dat)
  • Click a world → load it → navigate to WorldScreen
  • Delete button with confirmation
  • Back button returns to singleplayer

GameSession changes

  • GameSession.init() currently always generates a fresh world
  • Add path for loading from save: init() takes optional save_dir parameter
  • If save_dir provided: World uses SaveManager to load chunks instead of generating
  • If no save_dir: current behavior (fresh generation)

Implementation Plan

Step 1: WorldListScreen

// src/game/screens/world_list.zig
const WorldListScreen = struct {
    worlds: []WorldEntry,
    selected: ?usize,
    scroll_offset: f32,

    const WorldEntry = struct {
        name: []const u8,
        seed: u64,
        last_played: i64,
        generator: []const u8,
        dir_path: []const u8,
    };
};
  • onEnter(): scan save directory, read each level.dat, populate list
  • update(): handle scroll, selection, click
  • draw(): render list with UISystem (same pattern as other screens)
  • Back button, Load button, Delete button

Step 2: SingleplayerScreen modifications

  • Add "LOAD WORLD" button below "CREATE"
  • On click → screen_manager.push(WorldListScreen)
  • Keep existing create flow unchanged

Step 3: GameSession load path

Step 4: Delete world

  • Confirmation dialog: "Delete world ''? This cannot be undone."
  • On confirm: recursively delete save directory
  • Refresh world list

Files to Create

  • src/game/screens/world_list.zig — new screen

Files to Modify

  • src/game/screens/singleplayer.zig — add "LOAD WORLD" button
  • src/game/session.zig — load path, save_dir parameter
  • src/game/screen.zig — register new screen type (if needed)
  • src/tests.zig or src/game/screen_tests.zig — tests for WorldListScreen

Testing

  • Create world → close → load world → blocks are where you left them
  • World list shows all saves with correct info
  • Delete world removes directory and refreshes list
  • Cancel delete does nothing
  • Loading invalid/corrupt save shows error, doesn't crash
  • Empty save directory shows helpful message
  • Back button returns to singleplayer screen

Roadmap: docs/PERFORMANCE_ROADMAP.md — Batch 3, Issue 2C

Metadata

Metadata

Assignees

No one assigned

    Labels

    batch-3Batch 3: IntegrationbugSomething isn't workingdocumentationImprovements or additions to documentationenhancementNew feature or requestgamehotfixquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions