chunk_saving_super/
chunk_saving_super.rs

1//! Example: SuperChunk saving (multiple chunks per file)
2//!
3//! Tests batch save/load with grouped chunk files.
4
5use bevy::prelude::*;
6use chunky_bevy::prelude::*;
7use serde::{Deserialize, Serialize};
8
9fn main() {
10    App::new()
11        .add_plugins(DefaultPlugins)
12        .add_plugins(ChunkyPlugin::default())
13        .add_plugins(ChunkSavingPlugin::new("saves/super_world").with_style(
14            SaveStyle::SuperChunk {
15                size: UVec3::splat(4),
16            },
17        ))
18        .register_chunk_data::<SimpleData>()
19        .add_systems(Startup, setup)
20        .add_systems(Update, (handle_input, visualize_chunks))
21        .run();
22}
23
24#[derive(Component, Serialize, Deserialize, Clone, Debug)]
25struct SimpleData {
26    id: i32,
27}
28
29fn setup(mut commands: Commands) {
30    // Spawn a grid of chunks
31    for x in -4..=4 {
32        for z in -4..=4 {
33            let pos = IVec3::new(x, 0, z);
34            commands.spawn((Chunk, ChunkPos(pos), SimpleData { id: x * 100 + z }));
35        }
36    }
37
38    commands.spawn((
39        Camera3d::default(),
40        Transform::from_xyz(0.0, 120.0, 0.0).looking_at(Vec3::ZERO, Vec3::Z),
41    ));
42
43    info!("SuperChunk mode: 4x4x4 chunks per file");
44    info!("Press S for batch save, L for batch load, C to clear, I to inspect files");
45}
46
47fn handle_input(
48    keys: Res<ButtonInput<KeyCode>>,
49    world: &World,
50    mut commands: Commands,
51    chunks: Query<Entity, With<Chunk>>,
52    registry: Res<ChunkDataRegistry>,
53    config: Res<ChunkSaveConfig>,
54) {
55    // Batch save all chunks
56    if keys.just_pressed(KeyCode::KeyS) {
57        let entities: Vec<_> = chunks.iter().collect();
58        match registry.save_batch(world, &entities, &config) {
59            Ok(_) => info!("Batch saved {} chunks", entities.len()),
60            Err(e) => error!("Batch save failed: {:?}", e),
61        }
62    }
63
64    // Batch load from super-chunk at origin
65    if keys.just_pressed(KeyCode::KeyL) {
66        // Load super-chunks that cover our area
67        for sx in -1..=1 {
68            for sz in -1..=1 {
69                // Any position in the super-chunk region works
70                let pos = IVec3::new(sx * 4, 0, sz * 4);
71                match registry.load_batch(&mut commands, &config, pos) {
72                    Ok(loaded) => {
73                        info!(
74                            "Loaded {} chunks from super-chunk ({}, {})",
75                            loaded.len(),
76                            sx,
77                            sz
78                        );
79                    }
80                    Err(e) => warn!("No super-chunk at ({}, {}): {:?}", sx, sz, e),
81                }
82            }
83        }
84    }
85
86    // Clear all chunks
87    if keys.just_pressed(KeyCode::KeyC) {
88        for entity in chunks.iter() {
89            commands.entity(entity).despawn();
90        }
91        info!("Cleared all chunks");
92    }
93
94    // Inspect save directory
95    if keys.just_pressed(KeyCode::KeyI) {
96        match std::fs::read_dir(&config.base_path) {
97            Ok(entries) => {
98                info!("Save files:");
99                for entry in entries.flatten() {
100                    let path = entry.path();
101                    let size = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
102                    info!("  {:?} ({} bytes)", path.file_name().unwrap(), size);
103                }
104            }
105            Err(_) => info!("No save directory yet"),
106        }
107    }
108}
109
110fn visualize_chunks(
111    chunks: Query<(&ChunkPos, Option<&SimpleData>), With<Chunk>>,
112    config: Res<ChunkSaveConfig>,
113    mut gizmos: Gizmos,
114) {
115    let chunk_size = 10.0;
116
117    // Draw each chunk
118    for (pos, data) in chunks.iter() {
119        let world_pos = pos.0.as_vec3() * chunk_size + Vec3::new(5.0, 0.0, 5.0);
120
121        // Color based on whether it has data
122        let color = if data.is_some() {
123            Color::srgb(0.2, 0.8, 0.2)
124        } else {
125            Color::srgb(0.8, 0.2, 0.2)
126        };
127
128        gizmos.rect(
129            Isometry3d::new(
130                world_pos,
131                Quat::from_rotation_x(std::f32::consts::FRAC_PI_2),
132            ),
133            Vec2::splat(chunk_size - 0.5),
134            color,
135        );
136    }
137
138    // Draw super-chunk boundaries
139    if let SaveStyle::SuperChunk { size } = &config.style {
140        let super_size = size.as_vec3() * chunk_size;
141
142        for sx in -1..=1 {
143            for sz in -1..=1 {
144                let super_pos = Vec3::new(
145                    sx as f32 * super_size.x + super_size.x / 2.0,
146                    0.5,
147                    sz as f32 * super_size.z + super_size.z / 2.0,
148                );
149
150                // Yellow outline for super-chunk boundaries
151                gizmos.rect(
152                    Isometry3d::new(
153                        super_pos,
154                        Quat::from_rotation_x(std::f32::consts::FRAC_PI_2),
155                    ),
156                    Vec2::new(super_size.x - 0.2, super_size.z - 0.2),
157                    Color::srgb(1.0, 1.0, 0.0),
158                );
159            }
160        }
161    }
162}