From 2c5e7ec0998c38c706e132fd29a4773601f8bb0d Mon Sep 17 00:00:00 2001 From: terrarier2111 <58695553+terrarier2111@users.noreply.github.com> Date: Fri, 8 Mar 2024 23:02:47 +0100 Subject: [PATCH] Start moving to ecs (#319) --- src/entity/block_entity/mod.rs | 11 +- src/entity/block_entity/sign.rs | 9 +- src/entity/mod.rs | 20 ++- src/entity/player.rs | 7 +- src/particle/block_break_effect.rs | 7 +- src/server/mod.rs | 188 +++++++++++++++++++---------- src/server/sun.rs | 4 +- src/world/mod.rs | 36 ++++-- 8 files changed, 172 insertions(+), 110 deletions(-) diff --git a/src/entity/block_entity/mod.rs b/src/entity/block_entity/mod.rs index 169fa150..c79fda46 100644 --- a/src/entity/block_entity/mod.rs +++ b/src/entity/block_entity/mod.rs @@ -1,12 +1,11 @@ pub mod sign; -use crate::ecs; use crate::shared::Position; use crate::world::block::Block; use bevy_ecs::prelude::*; -pub fn add_systems(m: &mut ecs::Manager) { - sign::add_systems(m); +pub fn add_systems(sched: &mut Schedule) { + sign::add_systems(sched); } pub enum BlockEntityType { @@ -38,12 +37,12 @@ impl BlockEntityType { } } - pub fn create_entity(&self, m: &mut ecs::Manager, pos: Position) -> Entity { - let mut e = m.world.spawn_empty(); + pub fn create_entity(&self, cmds: &mut Commands, pos: Position) -> Entity { + let mut e = cmds.spawn_empty(); e.insert(pos); let e = e.id(); match *self { - BlockEntityType::Sign => sign::init_entity(m, e), + BlockEntityType::Sign => sign::init_entity(cmds, e), } e } diff --git a/src/entity/block_entity/sign.rs b/src/entity/block_entity/sign.rs index 78af5c5d..ede8c85f 100644 --- a/src/entity/block_entity/sign.rs +++ b/src/entity/block_entity/sign.rs @@ -1,4 +1,4 @@ -use crate::ecs::{self, SystemExecStage}; +use crate::ecs::SystemExecStage; use crate::format::{self, Component}; use crate::render; use crate::render::model::{self, FormatState}; @@ -9,8 +9,7 @@ use crate::world::block::Block; use bevy_ecs::prelude::*; use std::sync::Arc; -pub fn add_systems(m: &mut ecs::Manager) { - let mut sched = m.entity_schedule.write(); +pub fn add_systems(sched: &mut Schedule) { sched /*sync*/ .add_systems( render_sign @@ -24,8 +23,8 @@ pub fn add_systems(m: &mut ecs::Manager) { ); } -pub fn init_entity(m: &mut ecs::Manager, e: Entity) { - m.world.get_entity_mut(e).unwrap().insert(SignInfo { +pub fn init_entity(cmds: &mut Commands, e: Entity) { + cmds.get_entity(e).unwrap().insert(SignInfo { model: None, lines: [ Component::new(format::ComponentType::new("", None)), diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 7c7137f3..cb29d30a 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -55,19 +55,16 @@ srel!(28.0, 20.0, 4.0, 12.0), // East | 0 1 0 | 0 0 1 OR 1 0 1 | 0 0 1 [0.0, 0.0, 0.0, 1.0], */ -pub fn add_systems(m: &mut Manager) { - m.schedule - .write() - .add_systems(systems::update_last_position.in_set(SystemExecStage::Normal)); - - player::add_systems(m); - let mut entity_sched = m.schedule.write(); - entity_sched +pub fn add_systems(sched: &mut Schedule) { + sched.add_systems(systems::update_last_position.in_set(SystemExecStage::Normal)); + + player::add_systems(sched); + sched .add_systems(systems::apply_velocity.in_set(SystemExecStage::Normal)) .add_systems(systems::apply_gravity.in_set(SystemExecStage::Normal)) .add_systems(systems::apply_digging.in_set(SystemExecStage::Normal)); - entity_sched /*sync*/ + sched /*sync*/ .add_systems( systems::lerp_position .in_set(SystemExecStage::Render) @@ -84,9 +81,8 @@ pub fn add_systems(m: &mut Manager) { .after(SystemExecStage::Normal), ); - drop(entity_sched); - block_entity::add_systems(m); - crate::particle::block_break_effect::add_systems(m); + block_entity::add_systems(sched); + crate::particle::block_break_effect::add_systems(sched); } /// Location of an entity in the world. diff --git a/src/entity/player.rs b/src/entity/player.rs index 9f1b2fdd..b5e14c01 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -26,17 +26,16 @@ use std::hash::BuildHasherDefault; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -pub fn add_systems(m: &mut Manager) { +pub fn add_systems(sched: &mut Schedule) { // TODO: Check sync/async usage! - m.schedule.write().add_systems( + sched.add_systems( handle_movement .in_set(SystemExecStage::Render) .after(SystemExecStage::Normal), ); // let sys = ParticleRenderer::new(m); // m.add_render_system(sys); - let mut entity_sched = m.schedule.write(); - entity_sched + sched .add_systems( update_render_players .in_set(SystemExecStage::Render) diff --git a/src/particle/block_break_effect.rs b/src/particle/block_break_effect.rs index 97c8694f..968163d3 100644 --- a/src/particle/block_break_effect.rs +++ b/src/particle/block_break_effect.rs @@ -1,4 +1,4 @@ -use crate::ecs::{Manager, SystemExecStage}; +use crate::ecs::SystemExecStage; use crate::render; use crate::render::model::ModelHandle; use crate::render::{model, Renderer}; @@ -7,11 +7,10 @@ use bevy_ecs::prelude::*; use cgmath::{Decomposed, Matrix4, Quaternion, Rad, Rotation3, Vector3}; use std::sync::Arc; -pub fn add_systems(m: &mut Manager) { +pub fn add_systems(sched: &mut Schedule) { // TODO: Check sync/async usage! /*sync*/ - m.entity_schedule - .write() + sched .add_systems( effect_added .in_set(SystemExecStage::Render) diff --git a/src/server/mod.rs b/src/server/mod.rs index 998fabb4..decf9bd0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::ecs::Manager; +use crate::ecs::{Manager, SystemExecStage}; use crate::entity; use crate::entity::player::{create_local, PlayerModel, PlayerMovement}; use crate::entity::{EntityType, GameInfo, Gravity, MouseButtons, TargetPosition, TargetRotation}; @@ -40,7 +40,8 @@ use atomic_float::AtomicF64; use base64::engine::general_purpose::STANDARD; use base64::Engine; use bevy_ecs::prelude::Entity; -use bevy_ecs::system::Resource; +use bevy_ecs::schedule::{IntoSystemConfigs, Schedule}; +use bevy_ecs::system::{Commands, Res, ResMut, Resource}; use cgmath::prelude::*; use cgmath::Vector3; use crossbeam_channel::unbounded; @@ -69,6 +70,8 @@ use std::sync::Arc; use std::thread; use std::time::{SystemTime, UNIX_EPOCH}; +use self::sun::SunModel; + pub mod plugin_messages; mod sun; pub mod target; @@ -79,7 +82,9 @@ pub struct DisconnectData { just_disconnected: bool, } +#[derive(Resource)] struct WorldData { + // FIXME: move to world? world_age: i64, // move to world? world_time: f64, // move to world? world_time_target: f64, // move to world? @@ -108,7 +113,6 @@ pub struct Server { pub world: Arc, pub entities: Arc>, - world_data: Arc>, resources: Arc>, version: AtomicUsize, @@ -122,14 +126,11 @@ pub struct Server { entity_tick_timer: AtomicF64, pub received_chat_at: ArcSwapOption, - sun_model: RwLock>, target_info: Arc>, pub render_list_computer: Sender, pub render_list_computer_notify: Receiver, pub hud_context: Arc>, pub inventory_context: Arc>, - fps: AtomicU32, - fps_start: AtomicU64, pub dead: AtomicBool, just_died: AtomicBool, last_chat_open: AtomicBool, @@ -893,8 +894,6 @@ impl Server { ))); let mut entities = Manager::default(); // FIXME: fix threading modes (make some systems execute in parallel and others in sync) - entities.world.insert_resource(entity::GameInfo::new()); - entities.world.insert_resource(WorldResource(world.clone())); entities .world .insert_resource(RendererResource(renderer.clone())); @@ -902,10 +901,15 @@ impl Server { .world .insert_resource(ScreenSystemResource(screen_sys.clone())); entities.world.insert_resource(ConnResource(conn.clone())); + entities.world.insert_resource(entity::GameInfo::new()); + entities.world.insert_resource(WorldResource(world.clone())); entities .world .insert_resource(InventoryContextResource(inventory_context.clone())); - entity::add_systems(&mut entities); + entities.world.insert_resource(DeltaResource(0.0)); + entities.world.insert_resource(WorldData::default()); + entity::add_systems(&mut entities.schedule.write()); + add_systems(&mut entities.schedule.write()); hud_context.write().slots = Some(inventory_context.read().base_slots.clone()); @@ -920,7 +924,6 @@ impl Server { disconnect_data: Arc::new(RwLock::new(DisconnectData::default())), world, - world_data: Arc::new(RwLock::new(WorldData::default())), version: AtomicUsize::new(version), resources, @@ -936,15 +939,12 @@ impl Server { tick_timer: AtomicF64::new(0.0), entity_tick_timer: AtomicF64::new(0.0), received_chat_at: ArcSwapOption::new(None), - sun_model: RwLock::new(None), target_info: Arc::new(RwLock::new(target::Info::new())), render_list_computer, render_list_computer_notify, hud_context, inventory_context, - fps: AtomicU32::new(0), - fps_start: AtomicU64::new(0), dead: AtomicBool::new(false), just_died: AtomicBool::new(false), last_chat_open: AtomicBool::new(false), @@ -988,27 +988,39 @@ impl Server { self.entities.write().world.despawn(*entity.1); } } - self.sun_model.write().take(); + self.entities + .write() + .world + .remove_resource::(); + // FIXME: remove other resources! } pub fn is_connected(&self) -> bool { - return self.conn.read().is_some(); + self.conn.read().is_some() } pub fn tick(&self, delta: f64, game: &mut Game) { - let renderer = self.renderer.clone(); - let start = SystemTime::now(); - let time = start.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64; // FIXME: use safer conversion - if self.fps_start.load(Ordering::Acquire) + 1000 < time { - self.hud_context - .write() - .update_fps(self.fps.load(Ordering::Acquire)); - self.fps_start.store(time, Ordering::Release); - self.fps.store(0, Ordering::Release); - } else { - self.fps - .store(self.fps.load(Ordering::Acquire) + 1, Ordering::Release); + { + let mut entities = self.entities.write(); + // FIXME: is there another way to do this? + entities.world.resource_mut::().0 = delta; + let start = SystemTime::now(); + let time = start.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64; // FIXME: use safer conversion + let fps_res = entities.world.get_resource::().unwrap(); + if fps_res.0.frame_start.load(Ordering::Acquire) + 1000 < time { + self.hud_context + .write() + .update_fps(fps_res.0.fps.load(Ordering::Acquire)); + fps_res.0.frame_start.store(time, Ordering::Release); + fps_res.0.fps.store(0, Ordering::Release); + } else { + fps_res + .0 + .fps + .store(fps_res.0.fps.load(Ordering::Acquire) + 1, Ordering::Release); + } } + let renderer = self.renderer.clone(); let chat_open = self.chat_open.load(Ordering::Acquire); if chat_open != self.last_chat_open.load(Ordering::Acquire) { self.last_chat_open.store(chat_open, Ordering::Release); @@ -1025,13 +1037,15 @@ impl Server { self.world.flag_dirty_all(); } { - // TODO: Check if the world type actually needs a sun - if self.sun_model.read().is_none() { - self.sun_model - .write() - .replace(sun::SunModel::new(renderer.clone())); + { + let mut entities = self.entities.write(); + if !entities.world.contains_resource::() { + // TODO: Check if the world type actually needs a sun + entities + .world + .insert_resource(SunModelResource(SunModel::new(renderer.clone()))); + } } - // Copy to camera if let Some(player) = self.player.load().as_ref() { let entities = self.entities.read(); @@ -1068,15 +1082,9 @@ impl Server { ); } - self.update_time(renderer.clone(), delta); - if let Some(sun_model) = self.sun_model.write().as_mut() { - sun_model.tick( - renderer.clone(), - self.world_data.read().world_time, - self.world_data.read().world_age, - ); - } - self.world.tick(&mut self.entities.write()); + self.update_time(&renderer); + // FIXME: tick sun in between! + // self.world.tick(&mut self.entities.write()); if self.player.load().as_ref().is_some() { if self.just_died.load(Ordering::Acquire) { @@ -1141,32 +1149,21 @@ impl Server { self.target_info.clone().write().clear(renderer); }*/ - fn update_time(&self, renderer: Arc, delta: f64) { - let mut world_data = self.world_data.write(); - if world_data.tick_time { - world_data.world_time_target += delta / 3.0; - let time = world_data.world_time_target; - world_data.world_time_target = (24000.0 + time) % 24000.0; - let mut diff = world_data.world_time_target - world_data.world_time; - if diff < -12000.0 { - diff += 24000.0 - } else if diff > 12000.0 { - diff -= 24000.0 - } - world_data.world_time += diff * (1.5 / 60.0) * delta; - let time = world_data.world_time; - world_data.world_time = (24000.0 + time) % 24000.0; - } else { - let time = world_data.world_time_target; - world_data.world_time = time; - } - drop(world_data); + fn update_time(&self, renderer: &Arc) { renderer.light_data.lock().sky_offset = self.calculate_sky_offset(); } fn calculate_sky_offset(&self) -> f32 { use std::f32::consts::PI; - let mut offset = ((1.0 + self.world_data.read().world_time as f32) / 24000.0) - 0.25; + let mut offset = ((1.0 + + self + .entities + .read() + .world + .resource::() + .world_time as f32) + / 24000.0) + - 0.25; if offset < 0.0 { offset += 1.0; } else if offset > 1.0 { @@ -1709,7 +1706,9 @@ impl Server { } fn on_time_update(&self, time_update: mapped_packet::play::clientbound::TimeUpdate) { - let mut world_data = self.world_data.write(); + // FIXME: use events that are passed to the world instead of locking up! + let mut entities = self.entities.write(); + let mut world_data = entities.world.resource_mut::(); world_data.world_age = time_update.time_of_day; world_data.world_time_target = (time_update.time_of_day % 24000) as f64; if world_data.world_time_target < 0.0 { @@ -2445,3 +2444,64 @@ pub struct ConnResource(pub Arc>>); #[derive(Resource)] pub struct InventoryContextResource(pub Arc>); + +#[derive(Resource)] +pub struct RenderCtxResource(pub Arc); + +pub struct RenderCtx { + pub fps: AtomicU32, + pub frame_start: AtomicU64, +} + +#[derive(Resource)] +pub struct SunModelResource(pub SunModel); + +#[derive(Resource)] +pub struct TargetResource(pub Arc>); + +#[derive(Resource)] +pub struct DeltaResource(pub f64); + +fn tick_sun( + mut sun: ResMut, + renderer: Res, + world_data: ResMut, +) { + sun.0.tick( + renderer.0.clone(), + world_data.world_time, + world_data.world_age, + ); +} + +fn tick_world(mut commands: Commands, world: Res) { + world.0.tick(&mut commands); +} + +fn tick_time(mut world_data: ResMut, delta: Res) { + let delta = delta.0; + if world_data.tick_time { + world_data.world_time_target += delta / 3.0; + let time = world_data.world_time_target; + world_data.world_time_target = (24000.0 + time) % 24000.0; + let mut diff = world_data.world_time_target - world_data.world_time; + if diff < -12000.0 { + diff += 24000.0 + } else if diff > 12000.0 { + diff -= 24000.0 + } + world_data.world_time += diff * (1.5 / 60.0) * delta; + let time = world_data.world_time; + world_data.world_time = (24000.0 + time) % 24000.0; + } else { + let time = world_data.world_time_target; + world_data.world_time = time; + } +} + +fn add_systems(sched: &mut Schedule) { + sched + .add_systems(tick_sun.in_set(SystemExecStage::Render)) + .add_systems(tick_world.in_set(SystemExecStage::Normal)) + .add_systems(tick_time.in_set(SystemExecStage::Normal)); +} diff --git a/src/server/sun.rs b/src/server/sun.rs index e75c0e0a..4f84917f 100644 --- a/src/server/sun.rs +++ b/src/server/sun.rs @@ -14,7 +14,7 @@ const SIZE: f32 = 50.0; impl SunModel { pub fn new(renderer: Arc) -> SunModel { SunModel { - sun: SunModel::generate_sun(renderer.clone()), + sun: SunModel::generate_sun(&renderer), moon: SunModel::generate_moon(renderer, 0), last_phase: 0, } @@ -62,7 +62,7 @@ impl SunModel { } } - pub fn generate_sun(renderer: Arc) -> model::ModelHandle { + pub fn generate_sun(renderer: &Arc) -> model::ModelHandle { let tex = render::Renderer::get_texture(renderer.get_textures_ref(), "environment/sun"); renderer.models.lock().create_model( model::SUN, diff --git a/src/world/mod.rs b/src/world/mod.rs index 816768c6..f1d9b26d 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -13,7 +13,10 @@ // limitations under the License. use arc_swap::ArcSwap; +use bevy_ecs::entity::Entity; +use bevy_ecs::system::{Command, Commands}; pub use leafish_blocks as block; +use leafish_protocol::format::Component; use leafish_protocol::nbt::NamedTag; use crate::shared::Position; @@ -237,14 +240,14 @@ impl World { } #[allow(clippy::verbose_bit_mask)] // "llvm generates better code" for updates_performed & 0xFFF "on x86" - pub fn tick(&self, m: &mut ecs::Manager) { + pub fn tick(&self, cmds: &mut Commands) { while let Ok(action) = self.block_entity_actions.1.try_recv() { match action { BlockEntityAction::Remove(pos) => { if let Some(chunk) = self.chunks.write().get_mut(&CPos(pos.x >> 4, pos.z >> 4)) { if let Some(entity) = chunk.block_entities.remove(&pos) { - m.world.despawn(entity); + cmds.entity(entity).despawn(); } } } @@ -253,13 +256,13 @@ impl World { { // Remove existing entity if let Some(entity) = chunk.block_entities.remove(&pos) { - m.world.despawn(entity); + cmds.entity(entity).despawn(); } let block = chunk.get_block(pos.x & 0xF, pos.y, pos.z & 0xF); if let Some(entity_type) = block_entity::BlockEntityType::get_block_entity(block) { - let entity = entity_type.create_entity(m, pos); + let entity = entity_type.create_entity(cmds, pos); chunk.block_entities.insert(pos, entity); } } @@ -268,15 +271,7 @@ impl World { let (pos, line1, line2, line3, line4) = *bx; if let Some(chunk) = self.chunks.write().get(&CPos(pos.x >> 4, pos.z >> 4)) { if let Some(entity) = chunk.block_entities.get(&pos) { - if let Some(mut sign) = m - .world - .get_entity_mut(*entity) - .unwrap() - .get_mut::() - { - sign.lines = [line1, line2, line3, line4]; - sign.dirty = true; - } + cmds.add(UpdateSignInfoCmd([line1, line2, line3, line4], *entity)); } } } @@ -1477,6 +1472,21 @@ impl Dimension { } } +struct UpdateSignInfoCmd([Component; 4], Entity); + +impl Command for UpdateSignInfoCmd { + fn apply(self, world: &mut bevy_ecs::world::World) { + let mut entity = world.get_entity_mut(self.1); + if let Some(mut info) = entity + .as_mut() + .and_then(|entity| entity.get_mut::()) + { + info.lines = self.0.clone(); + info.dirty = true; + } + } +} + #[cfg(test)] mod tests { use super::*;