From 808d1873bd7513f8fac6580edc62d836b1650ef8 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 11 Nov 2020 11:42:22 +0000 Subject: [PATCH] Began implementing rtsim --- common/src/event.rs | 1 + server/src/events/entity_manipulation.rs | 10 +++ server/src/events/mod.rs | 3 +- server/src/lib.rs | 10 ++- server/src/rtsim/load_chunks.rs | 17 +++++ server/src/rtsim/mod.rs | 80 +++++++++++++++++++++--- server/src/rtsim/state.rs | 20 ------ server/src/rtsim/unload_chunks.rs | 54 ++++++++++++++++ server/src/sys/terrain.rs | 13 +++- world/src/layer/tree.rs | 13 ++-- world/src/lib.rs | 1 - world/src/rtsim/mod.rs | 1 - world/src/util/random.rs | 4 ++ 13 files changed, 188 insertions(+), 39 deletions(-) create mode 100644 server/src/rtsim/load_chunks.rs delete mode 100644 server/src/rtsim/state.rs create mode 100644 server/src/rtsim/unload_chunks.rs delete mode 100644 world/src/rtsim/mod.rs diff --git a/common/src/event.rs b/common/src/event.rs index e9da671203..5d5c9a4444 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -37,6 +37,7 @@ pub enum ServerEvent { entity: EcsEntity, change: comp::HealthChange, }, + Delete(EcsEntity), Destroy { entity: EcsEntity, cause: comp::HealthSource, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 97e5f73c2e..8981f8414b 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -470,6 +470,16 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc */ } +/// Delete an entity without any special actions (this is generally used for +/// temporarily unloading an entity when it leaves the view distance). As much +/// as possible, this function should simply make an entity cease to exist. +pub fn handle_delete(server: &mut Server, entity: EcsEntity) { + let _ = server + .state_mut() + .delete_entity_recorded(entity) + .map_err(|e| error!(?e, ?entity, "Failed to delete destroyed entity")); +} + pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) { let state = &server.state; if vel.z <= -30.0 { diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index a4707a971e..afac60a788 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -8,7 +8,7 @@ use entity_creation::{ handle_loaded_character_data, handle_shockwave, handle_shoot, }; use entity_manipulation::{ - handle_buff, handle_damage, handle_destroy, handle_energy_change, handle_explosion, + handle_buff, handle_damage, handle_destroy, handle_delete, handle_energy_change, handle_explosion, handle_knockback, handle_land_on_ground, handle_level_up, handle_respawn, }; use group_manip::handle_group; @@ -83,6 +83,7 @@ impl Server { handle_knockback(&self, entity, impulse) }, ServerEvent::Damage { entity, change } => handle_damage(&self, entity, change), + ServerEvent::Delete(entity) => handle_delete(self, entity), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), ServerEvent::GroupManip(entity, manip) => handle_group(self, entity, manip), diff --git a/server/src/lib.rs b/server/src/lib.rs index f9f452d493..31033de097 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -492,7 +492,14 @@ impl Server { // 4) Tick the server's LocalState. // 5) Fetch any generated `TerrainChunk`s and insert them into the terrain. // in sys/terrain.rs - self.state.tick(dt, sys::add_server_systems, false); + self.state.tick( + dt, + |dispatcher_builder| { + sys::add_server_systems(dispatcher_builder); + rtsim::add_server_systems(dispatcher_builder); + }, + false, + ); let before_handle_events = Instant::now(); @@ -517,7 +524,6 @@ impl Server { // Tick the world self.world.tick(dt); - rtsim::tick(&mut self.state); let before_entity_cleanup = Instant::now(); diff --git a/server/src/rtsim/load_chunks.rs b/server/src/rtsim/load_chunks.rs new file mode 100644 index 0000000000..98d236b062 --- /dev/null +++ b/server/src/rtsim/load_chunks.rs @@ -0,0 +1,17 @@ +use super::*; +use common::event::{EventBus, ServerEvent}; +use specs::{Join, Read, ReadStorage, System, Write, WriteExpect}; + +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Read<'a, EventBus>, + WriteExpect<'a, RtSim>, + ); + + fn run(&mut self, (server_event_bus, mut rtsim): Self::SystemData) { + for chunk in std::mem::take(&mut rtsim.chunks_to_load) { + // TODO + } + } +} diff --git a/server/src/rtsim/mod.rs b/server/src/rtsim/mod.rs index b433d330bf..e307a10fd6 100644 --- a/server/src/rtsim/mod.rs +++ b/server/src/rtsim/mod.rs @@ -1,14 +1,78 @@ -pub mod state; +mod load_chunks; +mod unload_chunks; +use vek::*; +use world::util::Grid; use common::state::State; +use specs::{DispatcherBuilder, Component, WorldExt}; +use specs_idvs::IdvStorage; + +type EntityId = u64; + +pub struct RtSim { + chunks: Grid, + chunks_to_load: Vec>, + chunks_to_unload: Vec>, +} + +impl RtSim { + pub fn new(world_chunk_size: Vec2) -> Self { + Self { + chunks: Grid::populate_from(world_chunk_size.map(|e| e as i32), |_| Chunk { + is_loaded: false, + }), + chunks_to_load: Vec::new(), + chunks_to_unload: Vec::new(), + } + } + + pub fn hook_load_chunk(&mut self, key: Vec2) { + if let Some(chunk) = self.chunks.get_mut(key) { + if !chunk.is_loaded { + chunk.is_loaded = true; + self.chunks_to_load.push(key); + } + } + } + + pub fn hook_unload_chunk(&mut self, key: Vec2) { + if let Some(chunk) = self.chunks.get_mut(key) { + if chunk.is_loaded { + chunk.is_loaded = false; + self.chunks_to_unload.push(key); + } + } + } + + pub fn assimilate_entity(&mut self, entity: EntityId) { + // TODO + } + + pub fn update_entity(&mut self, entity: EntityId, pos: Vec3) { + // TODO + } +} + +pub struct Chunk { + is_loaded: bool, +} + +pub struct RtSimEntity(EntityId); + +impl Component for RtSimEntity { + type Storage = IdvStorage; +} + +const LOAD_CHUNK_SYS: &str = "rtsim_load_chunk_sys"; +const UNLOAD_CHUNK_SYS: &str = "rtsim_unload_chunk_sys"; + +pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { + dispatch_builder.add(load_chunks::Sys, LOAD_CHUNK_SYS, &[]); + dispatch_builder.add(unload_chunks::Sys, UNLOAD_CHUNK_SYS, &[]); +} pub fn init(state: &mut State, world: &world::World) { - state - .ecs_mut() - .insert(state::SimState::new(world.sim().get_size())); + state.ecs_mut().insert(RtSim::new(world.sim().get_size())); + state.ecs_mut().register::(); tracing::info!("Initiated real-time world simulation"); } - -pub fn tick(state: &mut State) { - // TODO -} diff --git a/server/src/rtsim/state.rs b/server/src/rtsim/state.rs deleted file mode 100644 index e8da77a74a..0000000000 --- a/server/src/rtsim/state.rs +++ /dev/null @@ -1,20 +0,0 @@ -use vek::*; -use world::util::Grid; - -pub struct SimState { - chunks: Grid, -} - -impl SimState { - pub fn new(world_chunk_size: Vec2) -> Self { - Self { - chunks: Grid::populate_from(world_chunk_size.map(|e| e as i32), |_| Chunk { - is_loaded: false, - }), - } - } -} - -pub struct Chunk { - is_loaded: bool, -} diff --git a/server/src/rtsim/unload_chunks.rs b/server/src/rtsim/unload_chunks.rs new file mode 100644 index 0000000000..8aee4b0818 --- /dev/null +++ b/server/src/rtsim/unload_chunks.rs @@ -0,0 +1,54 @@ +use super::*; +use common::{ + event::{EventBus, ServerEvent}, + terrain::TerrainGrid, + comp::Pos, +}; +use specs::{Join, Read, ReadStorage, System, Write, ReadExpect, WriteExpect, Entities}; + +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Read<'a, EventBus>, + WriteExpect<'a, RtSim>, + ReadExpect<'a, TerrainGrid>, + Entities<'a>, + ReadStorage<'a, RtSimEntity>, + ReadStorage<'a, Pos>, + ); + + fn run( + &mut self, + ( + server_event_bus, + mut rtsim, + terrain_grid, + entities, + rtsim_entities, + positions, + ): Self::SystemData, + ) { + let chunks = std::mem::take(&mut rtsim.chunks_to_unload); + + for (entity, rtsim_entity, pos) in ( + &entities, + &rtsim_entities, + &positions, + ).join() { + let key = terrain_grid.pos_key(pos.0.map(|e| e.floor() as i32)); + + if terrain_grid.get_key(key).is_some() { + break; + } else if chunks.contains(&key) { + // Assimilate the entity back into the simulation + rtsim.assimilate_entity(rtsim_entity.0); + } + + rtsim.update_entity(rtsim_entity.0, pos.0); + } + + for chunk in chunks { + + } + } +} diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 4cb75e0244..7764d220f8 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,5 +1,11 @@ use super::SysTimer; -use crate::{chunk_generator::ChunkGenerator, client::Client, presence::Presence, Tick}; +use crate::{ + chunk_generator::ChunkGenerator, + client::Client, + presence::Presence, + rtsim::RtSim, + Tick, +}; use common::{ comp::{self, bird_medium, item::tool::AbilityMap, Alignment, Pos}, event::{EventBus, ServerEvent}, @@ -32,6 +38,7 @@ impl<'a> System<'a> for Sys { WriteExpect<'a, ChunkGenerator>, WriteExpect<'a, TerrainGrid>, Write<'a, TerrainChanges>, + WriteExpect<'a, RtSim>, ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, @@ -47,6 +54,7 @@ impl<'a> System<'a> for Sys { mut chunk_generator, mut terrain, mut terrain_changes, + mut rtsim, positions, presences, clients, @@ -100,6 +108,7 @@ impl<'a> System<'a> for Sys { terrain_changes.modified_chunks.insert(key); } else { terrain_changes.new_chunks.insert(key); + rtsim.hook_load_chunk(key); } // Handle chunk supplement @@ -217,10 +226,12 @@ impl<'a> System<'a> for Sys { chunks_to_remove.push(chunk_key); } }); + for key in chunks_to_remove { // TODO: code duplication for chunk insertion between here and state.rs if terrain.remove(key).is_some() { terrain_changes.removed_chunks.insert(key); + rtsim.hook_unload_chunk(key); } chunk_generator.cancel_if_pending(key); diff --git a/world/src/layer/tree.rs b/world/src/layer/tree.rs index 9228646ba2..421bb97b20 100644 --- a/world/src/layer/tree.rs +++ b/world/src/layer/tree.rs @@ -48,20 +48,23 @@ pub fn apply_trees_to(canvas: &mut Canvas) { let tree = if let Some(tree) = tree_cache.entry(tree_wpos).or_insert_with(|| { let col = ColumnGen::new(info.land()).get((tree_wpos, info.index()))?; + let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0); + // Ensure that it's valid to place a tree here - if ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density - || col.alt < col.water_level + if col.alt < col.water_level || col.spawn_rate < 0.5 || col.water_dist.map(|d| d < 8.0).unwrap_or(false) || col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false) { return None; + } else if !is_quirky && ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density { + return None; } Some(Tree { pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32), model: { - let models: &'static [_] = if QUIRKY_RAND.get(seed) % 512 == 17 { + let models: &'static [_] = if is_quirky { if col.temp > CONFIG.desert_temp { &QUIRKY_DRY } else { @@ -69,8 +72,8 @@ pub fn apply_trees_to(canvas: &mut Canvas) { } } else { match col.forest_kind { - ForestKind::Oak if QUIRKY_RAND.get(seed) % 16 == 7 => &OAK_STUMPS, - ForestKind::Oak if QUIRKY_RAND.get(seed) % 19 == 7 => &FRUIT_TREES, + ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => &OAK_STUMPS, + ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => &FRUIT_TREES, ForestKind::Palm => &PALMS, ForestKind::Savannah => &ACACIAS, ForestKind::Oak => &OAKS, diff --git a/world/src/lib.rs b/world/src/lib.rs index 172c6f3f1d..0bca796afc 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -20,7 +20,6 @@ mod column; pub mod config; pub mod index; pub mod layer; -pub mod rtsim; pub mod sim; pub mod sim2; pub mod site; diff --git a/world/src/rtsim/mod.rs b/world/src/rtsim/mod.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/world/src/rtsim/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/world/src/util/random.rs b/world/src/util/random.rs index aaf6b208e3..f5c9ab3cd5 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -45,6 +45,10 @@ pub struct RandomPerm { impl RandomPerm { pub const fn new(seed: u32) -> Self { Self { seed } } + + pub fn chance(&self, perm: u32, chance: f32) -> bool { + (self.get(perm) % (1 << 16)) as f32 / ((1 << 16) as f32) < chance + } } impl Sampler<'static> for RandomPerm {