Began implementing rtsim

This commit is contained in:
Joshua Barretto 2020-11-11 11:42:22 +00:00
parent 9e290d59ae
commit 808d1873bd
13 changed files with 188 additions and 39 deletions

View File

@ -37,6 +37,7 @@ pub enum ServerEvent {
entity: EcsEntity,
change: comp::HealthChange,
},
Delete(EcsEntity),
Destroy {
entity: EcsEntity,
cause: comp::HealthSource,

View File

@ -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<f32>) {
let state = &server.state;
if vel.z <= -30.0 {

View File

@ -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),

View File

@ -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();

View File

@ -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<ServerEvent>>,
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
}
}
}

View File

@ -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<Chunk>,
chunks_to_load: Vec<Vec2<i32>>,
chunks_to_unload: Vec<Vec2<i32>>,
}
impl RtSim {
pub fn new(world_chunk_size: Vec2<u32>) -> 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<i32>) {
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<i32>) {
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<f32>) {
// TODO
}
}
pub struct Chunk {
is_loaded: bool,
}
pub struct RtSimEntity(EntityId);
impl Component for RtSimEntity {
type Storage = IdvStorage<Self>;
}
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::<RtSimEntity>();
tracing::info!("Initiated real-time world simulation");
}
pub fn tick(state: &mut State) {
// TODO
}

View File

@ -1,20 +0,0 @@
use vek::*;
use world::util::Grid;
pub struct SimState {
chunks: Grid<Chunk>,
}
impl SimState {
pub fn new(world_chunk_size: Vec2<u32>) -> Self {
Self {
chunks: Grid::populate_from(world_chunk_size.map(|e| e as i32), |_| Chunk {
is_loaded: false,
}),
}
}
}
pub struct Chunk {
is_loaded: bool,
}

View File

@ -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<ServerEvent>>,
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 {
}
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -1 +0,0 @@

View File

@ -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 {