From 800c3a5b4481af1a56dab812a79ab9f74ff222d8 Mon Sep 17 00:00:00 2001 From: Mubelotix Date: Tue, 21 Nov 2023 16:06:09 +0100 Subject: [PATCH] Automatically load necessary chunks --- minecraft-server/src/entities/player.rs | 4 +- .../src/player_handler/handshake.rs | 3 +- minecraft-server/src/server_behavior.rs | 4 +- minecraft-server/src/world/change.rs | 26 ++++++++++- minecraft-server/src/world/mod.rs | 44 ++++++++----------- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/minecraft-server/src/entities/player.rs b/minecraft-server/src/entities/player.rs index 38804479..a0406ac6 100644 --- a/minecraft-server/src/entities/player.rs +++ b/minecraft-server/src/entities/player.rs @@ -123,7 +123,7 @@ impl Handler { }).await.flatten() else { return }; // Tell the world about the changes - self.world.update_loaded_columns(self.eid, &loaded_columns).await; + self.world.update_loaded_columns(self.eid, loaded_columns).await; // Unload chunks for unloaded_column in unloaded_columns { @@ -137,8 +137,6 @@ impl Handler { for newly_loaded_column in newly_loaded_columns { if self.world.is_column_loaded(&newly_loaded_column).await { self.send_chunk(newly_loaded_column).await; - } else { - self.world.queue_loading(newly_loaded_column).await; } } } diff --git a/minecraft-server/src/player_handler/handshake.rs b/minecraft-server/src/player_handler/handshake.rs index 2ba250f2..38ffb78c 100644 --- a/minecraft-server/src/player_handler/handshake.rs +++ b/minecraft-server/src/player_handler/handshake.rs @@ -323,8 +323,7 @@ pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPl for cz in -3..=3 { let column_pos = ChunkColumnPosition { cx, cz }; if !world.is_column_loaded(&column_pos).await { - world.queue_loading(column_pos).await; - remaining_to_load += 1; + remaining_to_load += 1; // FIXME: what happens when chunks are loaded before that block but after we listen for them? } } } diff --git a/minecraft-server/src/server_behavior.rs b/minecraft-server/src/server_behavior.rs index b6bffdef..f27b757a 100644 --- a/minecraft-server/src/server_behavior.rs +++ b/minecraft-server/src/server_behavior.rs @@ -9,9 +9,7 @@ pub struct ServerBehavior { impl ServerBehavior { pub async fn init() -> ServerBehavior { let listener = TcpListener::bind("127.0.0.1:25567").await.expect("Failed to listen"); - let (world, receiver) = World::new(); - let world = Box::leak(Box::new(world)); - world.init(receiver); + let world = World::new(); // Send ticks to player handlers let world2: &World = world; diff --git a/minecraft-server/src/world/change.rs b/minecraft-server/src/world/change.rs index 848753a2..637ac4b5 100644 --- a/minecraft-server/src/world/change.rs +++ b/minecraft-server/src/world/change.rs @@ -445,8 +445,30 @@ impl WorldObserverManager { self.notify_entity_change(eid, position, None, change).await; } - pub async fn update_loaded_columns(&self, eid: Eid, loaded_chunks: &HashSet) { - // TODO + pub async fn update_loaded_columns(&self, eid: Eid, loaded_chunks: HashSet) { + let mut entities = self.trackers.write().await; + let Some(observer) = entities.get_mut(&eid) else {return}; + let unloaded_chunks = observer.blocks.difference(&loaded_chunks); + let newly_loaded_chunks = loaded_chunks.difference(&observer.blocks); + + let mut blocks = self.blocks.write().await; + let mut entities = self.entities.write().await; + for column in unloaded_chunks { + blocks.get_mut(column).map(|map| map.remove(&eid)); + entities.get_mut(column).map(|map| map.remove(&eid)); + } + for column in newly_loaded_chunks { + blocks.entry(column.clone()).or_default().insert(eid, observer.sender.clone()); + entities.entry(column.clone()).or_default().insert(eid, observer.sender.clone()); + } + observer.blocks = loaded_chunks.clone(); + } + + pub async fn get_all_needed_columns(&self) -> HashSet { + let blocks = self.blocks.read().await; + let mut all_needed_chunks = HashSet::new(); + all_needed_chunks.extend(blocks.keys().cloned()); + all_needed_chunks } pub async fn remove_subscriber(&self, eid: Eid) { diff --git a/minecraft-server/src/world/mod.rs b/minecraft-server/src/world/mod.rs index e6e7073e..e19ee196 100644 --- a/minecraft-server/src/world/mod.rs +++ b/minecraft-server/src/world/mod.rs @@ -17,28 +17,31 @@ pub struct World { entities: Entities, world_observer_manager: WorldObserverManager, - loading_task_sender: MpscSender } impl World { - pub fn new() -> (World, MpscReceiver) { - let (loading_task_sender, loading_task_receiver) = mpsc_channel(200); - - (World { + pub fn new() -> &'static World { + let world: &'static World = Box::leak(Box::new(World { map: WorldMap::new(4), entities: Entities::new(), world_observer_manager: WorldObserverManager::new(), - loading_task_sender, - }, loading_task_receiver) - } - - pub fn init(&'static self, mut loading_task_receiver: MpscReceiver) { + })); + + let world2 = world; tokio::spawn(async move { - while let Some(position) = loading_task_receiver.recv().await { - self.map.load(position.clone()).await; - self.world_observer_manager.notify_column_loaded(position).await; + let mut previously_loaded_columns = HashSet::new(); + loop { + let new_needed_chunks = world2.world_observer_manager.get_all_needed_columns().await; + for column_pos in new_needed_chunks.difference(&previously_loaded_columns) { + world2.map.load(column_pos.to_owned()).await; + world2.world_observer_manager.notify_column_loaded(column_pos.to_owned()).await; + } + previously_loaded_columns = new_needed_chunks; + tokio::time::sleep(Duration::from_millis(50)).await; } }); + + world } pub async fn get_block(&self, position: BlockPosition) -> Option { @@ -49,13 +52,6 @@ impl World { self.map.is_column_loaded(position).await } - pub async fn queue_loading(&'static self, position: ChunkColumnPosition) { - // TODO: propagate errors - if let Err(e) = self.loading_task_sender.send(position).await { - error!("Failed to queue loading task: {}", e); - } - } - pub async fn get_network_chunk(&self, position: ChunkPosition) -> Option { self.map.get_network_chunk(position).await } @@ -73,8 +69,8 @@ impl World { WorldObserverBuilder::new(eid, &self.world_observer_manager) } - pub async fn update_loaded_columns(&self, eid: Eid, loaded_columns: &HashSet) { - self.world_observer_manager.update_loaded_columns(eid, loaded_columns).await + pub async fn update_loaded_columns(&self, eid: Eid, loaded_columns: HashSet) { + self.world_observer_manager.update_loaded_columns(eid, loaded_columns).await; } pub async fn tick(&self, tick_id: usize) { @@ -151,9 +147,7 @@ mod tests { #[tokio::test] async fn test_world_notifications() { - let (world, receiver) = World::new(); - let world = Box::leak(Box::new(world)); - world.init(receiver); + let world = World::new(); let mut receiver1 = WorldObserverBuilder::new(1, &world.world_observer_manager).with_blocks_in_chunk(ChunkColumnPosition{cx: 0, cz: 0}).build().await; let mut receiver2 = WorldObserverBuilder::new(1, &world.world_observer_manager).with_blocks_in_chunk(ChunkColumnPosition{cx: 1, cz: 1}).build().await;