use crate::View; use crate::{inspector::HideFromInspector, prelude::*}; use bevy::ecs::system::SystemState; use bevy::ecs::world::CommandQueue; use bevy::tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task}; use bevy_ecs_tilemap::prelude::*; use rand::prelude::*; use rand_pcg::Pcg64; use rand_seeder::Seeder; use std::sync::mpsc::{channel, Receiver}; use std::sync::Mutex; #[derive(Component, Debug)] pub struct Tilemap; #[derive(Component, Debug)] pub struct TileChan(Mutex>); #[derive(Component, Debug)] pub struct TileLoaderTask(Task<()>); pub fn init(mut commands: Commands, assets: Res) { // TODO: I'm pretty determined to not have this sieze up the game despite the large number of entities being added. Should work with a "loading" screen and doing this in the background? let mut rng: Pcg64 = Seeder::from("default_seed").make_rng(); let size = TilemapSize::new(1024, 1024); let tile_size = TilemapTileSize::new(16., 16.); let grid_size = TilemapGridSize::new(16., 16.); let map_type = TilemapType::Square; let texture = TilemapTexture::Single(assets.load("img/Tileset Grass.png")); let storage = TileStorage::empty(size); let tilemap = commands .spawn(( Tilemap, Name::new("Tilemap"), TilemapBundle { grid_size, map_type, size, storage, texture, tile_size, transform: get_tilemap_center_transform(&size, &grid_size, &map_type, f32::MIN), ..Default::default() }, )) .id(); let pool = AsyncComputeTaskPool::get(); let (tx, rx) = channel::(); let task = pool.spawn(async move { for x in 0..size.x { for y in 0..size.y { let position = TilePos::new(x, y); let texture_index = TileTextureIndex(if rng.gen_range(0..1000) > 925 { rng.gen_range(0..(16 * 8)) } else { 0 }); tx.send(TileBundle { position, tilemap_id: TilemapId(tilemap), texture_index, ..Default::default() }) .unwrap(); } } return (); }); commands .entity(tilemap) .insert((TileLoaderTask(task), TileChan(Mutex::new(rx)))); } const MAX_TILES_TO_LOAD_IN_ONE_UPDATE: usize = 5_000; pub fn tile_loaders( world: &mut World, tilemap: QueryState>, storage: &mut QueryState<&mut TileStorage, With>, chan: QueryState<&TileChan, With>, task: QueryState<&mut TileLoaderTask, With>, params: &mut SystemState>>, ) { // TODO: this state should maybe be converted to a resource or something? let mut tiles_done = false; let mut task_done = false; type AddingTile = (HideFromInspector, TileBundle); type AddedTile<'a> = (Entity, &'a TilePos); let mut tiles_to_add: Vec = Vec::with_capacity(MAX_TILES_TO_LOAD_IN_ONE_UPDATE); let mut added_tiles: Vec = Vec::with_capacity(MAX_TILES_TO_LOAD_IN_ONE_UPDATE); // let mut task = task; // { // task_done = true; // let mut task = task_query.get_single(); // if let Ok(task) = task { // let poll = future::poll_once(task.0); // if block_on(poll).is_some() { // task_done = true; // // task is done, remove it so we don't poll it again // world.entity_mut(tilemap).remove::(); // } // } else { // next_view.set(View::InGame) // } // let (tilemap, mut storage) = entity_query.single_mut(); // let tiles: Vec = vec![]; // if let Ok(channel) = channel_query.get_single_mut() { // let channel = channel.0.lock().unwrap(); // tiles_done = false; // let mut counter = 0; // loop { // match channel.recv() { // Ok(tile) => { // let position = &tile.position; // let tile = world.spawn((HideFromInspector, tile)).id(); // storage.set(position, tile); // counter += 1; // if counter >= MAX_TILES_TO_LOAD_IN_ONE_UPDATE { // info!("Finished {}!", counter); // break; // } // } // Err(_) => { // world.entity_mut(tilemap).remove::(); // if counter > 0 { // info!("Finished {counter}! Probably finishing mapgen soon..."); // } // // the channel is likely closed, so let's note that and be done // tiles_done = true; // break; // } // } // } // } for tile in tiles_to_add { added_tiles.push((id, &tile.1.position)); let id = world.spawn(tile).id(); } // world.spawn_batch( // tiles // .into_iter() // .map(|t| (HideFromInspector, t)) // .into_iter(), // ); { let mut storage = storage.single_mut(world); // storage.set(& let mut next_view = params.get_mut(world); if task_done && tiles_done { next_view.set(View::InGame) } } } pub fn exit(mut commands: Commands, q: Query>) { for id in q.iter() { commands.entity(id).despawn_recursive(); } }