diff --git a/Cargo.lock b/Cargo.lock index a9bc358e08..3a1c24ede9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6888,9 +6888,11 @@ dependencies = [ name = "veloren-rtsim" version = "0.10.0" dependencies = [ - "ron 0.7.0", + "hashbrown 0.12.3", + "ron 0.8.0", "serde", "veloren-common", + "veloren-world", ] [[package]] @@ -6942,6 +6944,7 @@ dependencies = [ "veloren-common-systems", "veloren-network", "veloren-plugin-api", + "veloren-rtsim", "veloren-server-agent", "veloren-world", ] diff --git a/rtsim/Cargo.toml b/rtsim/Cargo.toml index 370da5a0d1..662da3835c 100644 --- a/rtsim/Cargo.toml +++ b/rtsim/Cargo.toml @@ -5,5 +5,7 @@ edition = "2021" [dependencies] common = { package = "veloren-common", path = "../common" } -ron = "0.7" +world = { package = "veloren-world", path = "../world" } +ron = "0.8" serde = { version = "1.0.110", features = ["derive"] } +hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] } diff --git a/rtsim/src/data/actor.rs b/rtsim/src/data/actor.rs new file mode 100644 index 0000000000..81893f29f5 --- /dev/null +++ b/rtsim/src/data/actor.rs @@ -0,0 +1,13 @@ +use hashbrown::HashMap; + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct ActorId { + pub idx: u32, + pub gen: u32, +} + +pub struct Actor {} + +pub struct Actors { + pub actors: HashMap, +} diff --git a/rtsim/src/data/helper.rs b/rtsim/src/data/helper.rs index 703d666d3f..6ea6b221d1 100644 --- a/rtsim/src/data/helper.rs +++ b/rtsim/src/data/helper.rs @@ -3,6 +3,7 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, }; +#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)] pub struct V(pub T); impl Serialize for V { @@ -22,12 +23,12 @@ impl<'de, T: Version> Deserialize<'de> for V { impl> Latest for V { fn to_unversioned(self) -> U { self.0.to_unversioned() } - fn from_unversioned(x: U) -> Self { Self(T::from_unversioned(x)) } + fn from_unversioned(x: &U) -> Self { Self(T::from_unversioned(x)) } } pub trait Latest { fn to_unversioned(self) -> T; - fn from_unversioned(x: T) -> Self; + fn from_unversioned(x: &T) -> Self; } pub trait Version: Sized + DeserializeOwned { diff --git a/rtsim/src/data/mod.rs b/rtsim/src/data/mod.rs index 140a0eb0f0..fd56da5a5e 100644 --- a/rtsim/src/data/mod.rs +++ b/rtsim/src/data/mod.rs @@ -1,10 +1,30 @@ pub mod helper; pub mod version; -pub mod world; +pub mod actor; +pub mod nature; -pub use self::world::World; +pub use self::{ + actor::{Actor, ActorId, Actors}, + nature::Nature, +}; + +use self::helper::Latest; +use ron::error::SpannedResult; +use serde::Deserialize; +use std::io::{Read, Write}; pub struct Data { - world: World, + pub nature: Nature, + pub actors: Actors, +} + +impl Data { + pub fn from_reader(reader: R) -> SpannedResult { + ron::de::from_reader(reader).map(version::LatestData::to_unversioned) + } + + pub fn write_to(&self, writer: W) -> Result<(), ron::Error> { + ron::ser::to_writer(writer, &version::LatestData::from_unversioned(self)) + } } diff --git a/rtsim/src/data/nature.rs b/rtsim/src/data/nature.rs new file mode 100644 index 0000000000..dbc53fab83 --- /dev/null +++ b/rtsim/src/data/nature.rs @@ -0,0 +1 @@ +pub struct Nature {} diff --git a/rtsim/src/data/version/actor.rs b/rtsim/src/data/version/actor.rs new file mode 100644 index 0000000000..490e0a14d2 --- /dev/null +++ b/rtsim/src/data/version/actor.rs @@ -0,0 +1,85 @@ +use super::*; +use crate::data::{Actor, ActorId, Actors}; +use hashbrown::HashMap; + +// ActorId + +impl Latest for ActorIdV0 { + fn to_unversioned(self) -> ActorId { + ActorId { + idx: self.idx, + gen: self.gen, + } + } + + fn from_unversioned(id: &ActorId) -> Self { + Self { + idx: id.idx, + gen: id.gen, + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ActorIdV0 { + pub idx: u32, + pub gen: u32, +} + +impl Version for ActorIdV0 { + type Prev = Bottom; + + fn migrate(x: Self::Prev) -> Self { match x {} } +} + +// Actor + +impl Latest for ActorV0 { + fn to_unversioned(self) -> Actor { Actor {} } + + fn from_unversioned(actor: &Actor) -> Self { Self {} } +} + +#[derive(Serialize, Deserialize, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ActorV0 {} + +impl Version for ActorV0 { + type Prev = Bottom; + + fn migrate(x: Self::Prev) -> Self { match x {} } +} + +// Actors + +impl Latest for ActorsV0 { + fn to_unversioned(self) -> Actors { + Actors { + actors: self + .actors + .into_iter() + .map(|(k, v)| (k.to_unversioned(), v.to_unversioned())) + .collect(), + } + } + + fn from_unversioned(actors: &Actors) -> Self { + Self { + actors: actors + .actors + .iter() + .map(|(k, v)| (Latest::from_unversioned(k), Latest::from_unversioned(v))) + .collect(), + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct ActorsV0 { + actors: HashMap, V>, +} + +impl Version for ActorsV0 { + type Prev = Bottom; + + fn migrate(x: Self::Prev) -> Self { match x {} } +} diff --git a/rtsim/src/data/version/mod.rs b/rtsim/src/data/version/mod.rs index af563e3403..cc86307cee 100644 --- a/rtsim/src/data/version/mod.rs +++ b/rtsim/src/data/version/mod.rs @@ -15,11 +15,17 @@ // conclusion that there's just no way to add the feature you want to add // without creating a new version of the data structure in question. // -// That said, here's how to make a change to one of the structures in this -// module, or submodules. +// Please note that in *very specific* cases, it is possible to make a change to +// an existing data structure that is backward-compatible. For example, adding a +// new variant to an enum or a new field to a struct (where said field is +// annotated with `#[serde(default)]`) is generally considered to be a +// backward-compatible change. +// +// That said, here's how to make a breaking change to one of the structures in +// this module, or submodules. // // 1) Duplicate the latest version of the data structure and the `Version` impl -// for it (later versions should be kept at the top of each file). +// for it (later versions should be kept at the top of each file). // // 2) Rename the duplicated version, incrementing the version number (i.e: V0 // becomes V1). @@ -38,7 +44,8 @@ // The *golden rule* is that, once merged to master, an old version's type must // not be changed! -pub mod world; +pub mod actor; +pub mod nature; use super::{ helper::{Bottom, Latest, Version, V}, @@ -46,23 +53,28 @@ use super::{ }; use serde::{Deserialize, Serialize}; -impl Latest for DataV0 { +pub type LatestData = DataV0; + +impl Latest for LatestData { fn to_unversioned(self) -> Data { Data { - world: self.world.to_unversioned(), + nature: self.nature.to_unversioned(), + actors: self.actors.to_unversioned(), } } - fn from_unversioned(data: Data) -> Self { + fn from_unversioned(data: &Data) -> Self { Self { - world: Latest::from_unversioned(data.world), + nature: Latest::from_unversioned(&data.nature), + actors: Latest::from_unversioned(&data.actors), } } } #[derive(Serialize, Deserialize)] pub struct DataV0 { - world: V, + nature: V, + actors: V, } impl Version for DataV0 { diff --git a/rtsim/src/data/version/nature.rs b/rtsim/src/data/version/nature.rs new file mode 100644 index 0000000000..1a10f0b929 --- /dev/null +++ b/rtsim/src/data/version/nature.rs @@ -0,0 +1,17 @@ +use super::*; +use crate::data::Nature; + +impl Latest for NatureV0 { + fn to_unversioned(self) -> Nature { Nature {} } + + fn from_unversioned(nature: &Nature) -> Self { Self {} } +} + +#[derive(Serialize, Deserialize)] +pub struct NatureV0 {} + +impl Version for NatureV0 { + type Prev = Bottom; + + fn migrate(x: Self::Prev) -> Self { match x {} } +} diff --git a/rtsim/src/data/version/world.rs b/rtsim/src/data/version/world.rs deleted file mode 100644 index 3c15eeb31e..0000000000 --- a/rtsim/src/data/version/world.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::*; -use crate::data::World; - -impl Latest for WorldV0 { - fn to_unversioned(self) -> World { World {} } - - fn from_unversioned(world: World) -> Self { Self {} } -} - -#[derive(Serialize, Deserialize)] -pub struct WorldV0 {} - -impl Version for WorldV0 { - type Prev = Bottom; - - fn migrate(x: Self::Prev) -> Self { match x {} } -} diff --git a/rtsim/src/data/world.rs b/rtsim/src/data/world.rs deleted file mode 100644 index 36b2386888..0000000000 --- a/rtsim/src/data/world.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct World {} diff --git a/rtsim/src/gen/mod.rs b/rtsim/src/gen/mod.rs new file mode 100644 index 0000000000..d1f9032a42 --- /dev/null +++ b/rtsim/src/gen/mod.rs @@ -0,0 +1,14 @@ +use crate::data::{Actors, Data, Nature}; +use hashbrown::HashMap; +use world::World; + +impl Data { + pub fn generate(world: &World) -> Self { + Self { + nature: Nature {}, + actors: Actors { + actors: HashMap::default(), + }, + } + } +} diff --git a/rtsim/src/lib.rs b/rtsim/src/lib.rs index dd3133d228..975ea54412 100644 --- a/rtsim/src/lib.rs +++ b/rtsim/src/lib.rs @@ -1,7 +1,10 @@ pub mod data; +pub mod gen; -/* -pub struct RtState<'a> { +use self::data::Data; +use std::sync::Arc; +use world::World; +pub struct RtState { + pub data: Data, } -*/ diff --git a/server/Cargo.toml b/server/Cargo.toml index 0cc75a54e1..6d3659fe7c 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -23,6 +23,7 @@ common-state = { package = "veloren-common-state", path = "../common/state" } common-systems = { package = "veloren-common-systems", path = "../common/systems" } common-net = { package = "veloren-common-net", path = "../common/net" } world = { package = "veloren-world", path = "../world" } +rtsim2 = { package = "veloren-rtsim", path = "../rtsim" } network = { package = "veloren-network", path = "../network", features = ["metrics", "compression", "quic"], default-features = false } server-agent = {package = "veloren-server-agent", path = "agent"} diff --git a/server/agent/src/data.rs b/server/agent/src/data.rs index 0443413009..c6a1f18eec 100644 --- a/server/agent/src/data.rs +++ b/server/agent/src/data.rs @@ -236,7 +236,7 @@ pub struct ReadData<'a> { pub light_emitter: ReadStorage<'a, LightEmitter>, #[cfg(feature = "worldgen")] pub world: ReadExpect<'a, Arc>, - pub rtsim_entities: ReadStorage<'a, RtSimEntity>, + // pub rtsim_entities: ReadStorage<'a, RtSimEntity>, pub buffs: ReadStorage<'a, Buffs>, pub combos: ReadStorage<'a, Combo>, pub active_abilities: ReadStorage<'a, ActiveAbilities>, diff --git a/server/src/error.rs b/server/src/error.rs index ac666301b3..e4a5c974cb 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -9,6 +9,7 @@ pub enum Error { StreamErr(StreamError), DatabaseErr(rusqlite::Error), PersistenceErr(PersistenceError), + RtsimError(ron::Error), Other(String), } @@ -41,6 +42,7 @@ impl Display for Error { Self::StreamErr(err) => write!(f, "Stream Error: {}", err), Self::DatabaseErr(err) => write!(f, "Database Error: {}", err), Self::PersistenceErr(err) => write!(f, "Persistence Error: {}", err), + Self::RtsimError(err) => write!(f, "Rtsim Error: {}", err), Self::Other(err) => write!(f, "Error: {}", err), } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index bddc7423ae..bb9913681e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -519,6 +519,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt } if should_delete { + /* if let Some(rtsim_entity) = state .ecs() .read_storage::() @@ -530,6 +531,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt .write_resource::() .destroy_entity(rtsim_entity.0); } + */ if let Err(e) = state.delete_entity_recorded(entity) { error!(?e, ?entity, "Failed to delete destroyed entity"); diff --git a/server/src/lib.rs b/server/src/lib.rs index f5f15e6c14..329857f909 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -30,6 +30,7 @@ pub mod persistence; mod pet; pub mod presence; pub mod rtsim; +pub mod rtsim2; pub mod settings; pub mod state_ext; pub mod sys; @@ -548,6 +549,7 @@ impl Server { let connection_handler = ConnectionHandler::new(network, &runtime); // Initiate real-time world simulation + /* #[cfg(feature = "worldgen")] { rtsim::init(&mut state, &world, index.as_index_ref()); @@ -555,6 +557,20 @@ impl Server { } #[cfg(not(feature = "worldgen"))] rtsim::init(&mut state); + */ + + // Init rtsim, loading it from disk if possible + #[cfg(feature = "worldgen")] + { + match rtsim2::RtSim::new(&world, data_dir.to_owned()) { + Ok(rtsim) => state.ecs_mut().insert(rtsim), + Err(err) => { + error!("Failed to load rtsim: {}", err); + return Err(Error::RtsimError(err)); + }, + } + weather::init(&mut state, &world); + } let server_constants = ServerConstants { day_cycle_coefficient: 1440.0 / settings.day_length, @@ -694,11 +710,18 @@ impl Server { add_local_systems(dispatcher_builder); sys::msg::add_server_systems(dispatcher_builder); sys::add_server_systems(dispatcher_builder); + /* #[cfg(feature = "worldgen")] { rtsim::add_server_systems(dispatcher_builder); weather::add_server_systems(dispatcher_builder); } + */ + #[cfg(feature = "worldgen")] + { + rtsim2::add_server_systems(dispatcher_builder); + weather::add_server_systems(dispatcher_builder); + } }, false, Some(&mut state_tick_metrics), @@ -805,6 +828,7 @@ impl Server { }; for entity in to_delete { + /* // Assimilate entities that are part of the real-time world simulation if let Some(rtsim_entity) = self .state @@ -818,6 +842,7 @@ impl Server { .write_resource::() .assimilate_entity(rtsim_entity.0); } + */ if let Err(e) = self.state.delete_entity_recorded(entity) { error!(?e, "Failed to delete agent outside the terrain"); diff --git a/server/src/rtsim2/mod.rs b/server/src/rtsim2/mod.rs new file mode 100644 index 0000000000..11bda686ad --- /dev/null +++ b/server/src/rtsim2/mod.rs @@ -0,0 +1,70 @@ +pub mod tick; + +use common::grid::Grid; +use common_ecs::{dispatch, System}; +use rtsim2::{data::Data, RtState}; +use specs::{DispatcherBuilder, WorldExt}; +use std::{fs::File, io, path::PathBuf, sync::Arc}; +use tracing::info; +use vek::*; +use world::World; + +pub struct RtSim { + file_path: PathBuf, + chunk_states: Grid, // true = loaded + state: RtState, +} + +impl RtSim { + pub fn new(world: &World, data_dir: PathBuf) -> Result { + let file_path = Self::get_file_path(data_dir); + + Ok(Self { + chunk_states: Grid::populate_from(world.sim().get_size().as_(), |_| false), + state: RtState { + data: { + info!("Looking for rtsim state in {}...", file_path.display()); + match File::open(&file_path) { + Ok(file) => { + info!("Rtsim state found. Attending to load..."); + Data::from_reader(file)? + }, + Err(e) if e.kind() == io::ErrorKind::NotFound => { + info!("No rtsim state found. Generating from initial world state..."); + Data::generate(&world) + }, + Err(e) => return Err(e.into()), + } + }, + }, + file_path, + }) + } + + fn get_file_path(mut data_dir: PathBuf) -> PathBuf { + let mut path = std::env::var("VELOREN_RTSIM") + .map(PathBuf::from) + .unwrap_or_else(|_| { + data_dir.push("rtsim"); + data_dir + }); + path.push("state.ron"); + path + } + + pub fn hook_load_chunk(&mut self, key: Vec2) { + if let Some(is_loaded) = self.chunk_states.get_mut(key) { + *is_loaded = true; + } + } + + pub fn hook_unload_chunk(&mut self, key: Vec2) { + if let Some(is_loaded) = self.chunk_states.get_mut(key) { + *is_loaded = false; + } + } +} + +pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { + dispatch::(dispatch_builder, &[]); +} diff --git a/server/src/rtsim2/tick.rs b/server/src/rtsim2/tick.rs new file mode 100644 index 0000000000..5222dcff0a --- /dev/null +++ b/server/src/rtsim2/tick.rs @@ -0,0 +1,179 @@ +#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out + +use super::*; +use crate::sys::terrain::NpcData; +use common::{ + comp, + event::{EventBus, ServerEvent}, + generation::{BodyBuilder, EntityConfig, EntityInfo}, + resources::{DeltaTime, Time}, +}; +use common_ecs::{Job, Origin, Phase, System}; +use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; +use std::sync::Arc; + +#[derive(Default)] +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Read<'a, DeltaTime>, + Read<'a, Time>, + Read<'a, EventBus>, + WriteExpect<'a, RtSim>, + ReadExpect<'a, Arc>, + ReadExpect<'a, world::IndexOwned>, + ); + + const NAME: &'static str = "rtsim::tick"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + + fn run( + _job: &mut Job, + (dt, time, server_event_bus, mut rtsim, world, index): Self::SystemData, + ) { + let rtsim = &mut *rtsim; + // rtsim.tick += 1; + + // Update unloaded rtsim entities, in groups at a time + /* + const TICK_STAGGER: usize = 30; + let entities_per_iteration = rtsim.entities.len() / TICK_STAGGER; + let mut to_reify = Vec::new(); + for (id, entity) in rtsim + .entities + .iter_mut() + .skip((rtsim.tick as usize % TICK_STAGGER) * entities_per_iteration) + .take(entities_per_iteration) + .filter(|(_, e)| !e.is_loaded) + { + if rtsim + .chunk_states + .get(entity.pos.xy()) + .copied() + .unwrap_or(false) + { + to_reify.push(id); + } else { + // Simulate behaviour + if let Some(travel_to) = &entity.controller.travel_to { + // Move towards target at approximate character speed + entity.pos += Vec3::from( + (travel_to.0.xy() - entity.pos.xy()) + .try_normalized() + .unwrap_or_else(Vec2::zero) + * entity.get_body().max_speed_approx() + * entity.controller.speed_factor, + ) * dt; + } + + if let Some(alt) = world + .sim() + .get_alt_approx(entity.pos.xy().map(|e| e.floor() as i32)) + { + entity.pos.z = alt; + } + } + // entity.tick(&time, &terrain, &world, &index.as_index_ref()); + } + */ + + // Tick entity AI each time if it's loaded + // for (_, entity) in rtsim.entities.iter_mut().filter(|(_, e)| + // e.is_loaded) { entity.last_time_ticked = time.0; + // entity.tick(&time, &terrain, &world, &index.as_index_ref()); + // } + + /* + let mut server_emitter = server_event_bus.emitter(); + for id in to_reify { + rtsim.reify_entity(id); + let entity = &rtsim.entities[id]; + let rtsim_entity = Some(RtSimEntity(id)); + + let body = entity.get_body(); + let spawn_pos = terrain + .find_space(entity.pos.map(|e| e.floor() as i32)) + .map(|e| e as f32) + + Vec3::new(0.5, 0.5, body.flying_height()); + + let pos = comp::Pos(spawn_pos); + + let event = if let comp::Body::Ship(ship) = body { + ServerEvent::CreateShip { + pos, + ship, + mountable: false, + agent: Some(comp::Agent::from_body(&body)), + rtsim_entity, + } + } else { + let entity_config_path = entity.get_entity_config(); + let mut loadout_rng = entity.loadout_rng(); + let ad_hoc_loadout = entity.get_adhoc_loadout(); + // Body is rewritten so that body parameters + // are consistent between reifications + let entity_config = EntityConfig::from_asset_expect_owned(entity_config_path) + .with_body(BodyBuilder::Exact(body)); + + let mut entity_info = EntityInfo::at(pos.0) + .with_entity_config(entity_config, Some(entity_config_path), &mut loadout_rng) + .with_lazy_loadout(ad_hoc_loadout); + // Merchants can be traded with + if let Some(economy) = entity.get_trade_info(&world, &index) { + entity_info = entity_info + .with_agent_mark(comp::agent::Mark::Merchant) + .with_economy(&economy); + } + match NpcData::from_entity_info(entity_info) { + NpcData::Data { + pos, + stats, + skill_set, + health, + poise, + inventory, + agent, + body, + alignment, + scale, + loot, + } => ServerEvent::CreateNpc { + pos, + stats, + skill_set, + health, + poise, + inventory, + agent, + body, + alignment, + scale, + anchor: None, + loot, + rtsim_entity, + projectile: None, + }, + // EntityConfig can't represent Waypoints at all + // as of now, and if someone will try to spawn + // rtsim waypoint it is definitely error. + NpcData::Waypoint(_) => unimplemented!(), + } + }; + server_emitter.emit(event); + } + + // Update rtsim with real entity data + for (pos, rtsim_entity, agent) in (&positions, &rtsim_entities, &mut agents).join() { + rtsim + .entities + .get_mut(rtsim_entity.0) + .filter(|e| e.is_loaded) + .map(|entity| { + entity.pos = pos.0; + agent.rtsim_controller = entity.controller.clone(); + }); + } + */ + } +} diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index f2789c6aca..2098dac8bb 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys { Read<'a, EventBus>, WriteStorage<'a, Agent>, WriteStorage<'a, Controller>, - WriteExpect<'a, RtSim>, + //WriteExpect<'a, RtSim>, ); const NAME: &'static str = "agent"; @@ -41,9 +41,9 @@ impl<'a> System<'a> for Sys { fn run( job: &mut Job, - (read_data, event_bus, mut agents, mut controllers, mut rtsim): Self::SystemData, + (read_data, event_bus, mut agents, mut controllers /* mut rtsim */): Self::SystemData, ) { - let rtsim = &mut *rtsim; + //let rtsim = &mut *rtsim; job.cpu_stats.measure(ParMode::Rayon); ( @@ -161,10 +161,12 @@ impl<'a> System<'a> for Sys { can_fly: body.map_or(false, |b| b.fly_thrust().is_some()), }; let health_fraction = health.map_or(1.0, Health::fraction); + /* let rtsim_entity = read_data .rtsim_entities .get(entity) .and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0)); + */ if traversal_config.can_fly && matches!(body, Some(Body::Ship(_))) { // hack (kinda): Never turn off flight airships @@ -226,7 +228,7 @@ impl<'a> System<'a> for Sys { // inputs. let mut behavior_data = BehaviorData { agent, - rtsim_entity, + // rtsim_entity, agent_data: data, read_data: &read_data, event_emitter: &mut event_emitter, @@ -240,6 +242,7 @@ impl<'a> System<'a> for Sys { debug_assert!(controller.inputs.look_dir.map(|e| !e.is_nan()).reduce_and()); }, ); + /* for (agent, rtsim_entity) in (&mut agents, &read_data.rtsim_entities).join() { // Entity must be loaded in as it has an agent component :) // React to all events in the controller @@ -258,5 +261,6 @@ impl<'a> System<'a> for Sys { } } } + */ } } diff --git a/server/src/sys/agent/behavior_tree.rs b/server/src/sys/agent/behavior_tree.rs index 6a3ebb37a0..eda52faedf 100644 --- a/server/src/sys/agent/behavior_tree.rs +++ b/server/src/sys/agent/behavior_tree.rs @@ -41,7 +41,7 @@ pub struct BehaviorData<'a, 'b, 'c> { pub agent: &'a mut Agent, pub agent_data: AgentData<'a>, // TODO: Move rtsim back into AgentData after rtsim2 when it has a separate crate - pub rtsim_entity: Option<&'a RtSimEntity>, + // pub rtsim_entity: Option<&'a RtSimEntity>, pub read_data: &'a ReadData<'a>, pub event_emitter: &'a mut Emitter<'c, ServerEvent>, pub controller: &'a mut Controller, @@ -277,6 +277,7 @@ fn target_if_attacked(bdata: &mut BehaviorData) -> bool { } // Remember this attack if we're an RtSim entity + /* if let Some(attacker_stats) = bdata.rtsim_entity.and(bdata.read_data.stats.get(attacker)) { @@ -284,6 +285,7 @@ fn target_if_attacked(bdata: &mut BehaviorData) -> bool { .agent .add_fight_to_memory(&attacker_stats.name, bdata.read_data.time.0); } + */ } } } @@ -316,9 +318,11 @@ fn untarget_if_dead(bdata: &mut BehaviorData) -> bool { if let Some(tgt_health) = bdata.read_data.healths.get(target) { // If target is dead, forget them if tgt_health.is_dead { + /* if let Some(tgt_stats) = bdata.rtsim_entity.and(bdata.read_data.stats.get(target)) { bdata.agent.forget_enemy(&tgt_stats.name); } + */ bdata.agent.target = None; return true; } @@ -496,7 +500,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool { bdata.controller, bdata.read_data, bdata.event_emitter, - will_ambush(bdata.rtsim_entity, &bdata.agent_data), + will_ambush(/* bdata.rtsim_entity */ None, &bdata.agent_data), ); } else { bdata.agent_data.handle_sounds_heard( @@ -639,7 +643,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { let BehaviorData { agent, agent_data, - rtsim_entity, + // rtsim_entity, read_data, event_emitter, controller, @@ -746,7 +750,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { controller, read_data, event_emitter, - will_ambush(*rtsim_entity, agent_data), + will_ambush(/* *rtsim_entity */None, agent_data), ); } @@ -764,9 +768,9 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { read_data, event_emitter, rng, - remembers_fight_with(*rtsim_entity, read_data, target), + remembers_fight_with(/* *rtsim_entity */None, read_data, target), ); - remember_fight(*rtsim_entity, read_data, agent, target); + remember_fight(/* *rtsim_entity */ None, read_data, agent, target); } } } @@ -775,6 +779,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { } fn will_ambush(rtsim_entity: Option<&RtSimEntity>, agent_data: &AgentData) -> bool { + // TODO: implement for rtsim2 agent_data .health .map_or(false, |h| h.current() / h.maximum() > 0.7) @@ -786,6 +791,7 @@ fn remembers_fight_with( read_data: &ReadData, other: EcsEntity, ) -> bool { + // TODO: implement for rtsim2 let name = || read_data.stats.get(other).map(|stats| stats.name.clone()); rtsim_entity.map_or(false, |rtsim_entity| { diff --git a/server/src/sys/agent/behavior_tree/interaction.rs b/server/src/sys/agent/behavior_tree/interaction.rs index 9d1088287f..3d37cc6d83 100644 --- a/server/src/sys/agent/behavior_tree/interaction.rs +++ b/server/src/sys/agent/behavior_tree/interaction.rs @@ -108,21 +108,17 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { match subject { Subject::Regular => { + /* if let Some(rtsim_entity) = &bdata.rtsim_entity { if matches!(rtsim_entity.kind, RtSimEntityKind::Prisoner) { agent_data.chat_npc("npc-speech-prisoner", event_emitter); - } else if let ( - Some((_travel_to, destination_name)), - Some(rtsim_entity), - ) = - (&agent.rtsim_controller.travel_to, &&bdata.rtsim_entity) - { + } else if let Some((_travel_to, destination_name) = &agent.rtsim_controller.travel_to { let personality = &rtsim_entity.brain.personality; let standard_response_msg = || -> String { if personality.will_ambush { format!( - "I'm heading to {}! Want to come along? We'll \ - make great travel buddies, hehe.", + "I'm heading to {}! Want to come along? We'll make \ + great travel buddies, hehe.", destination_name ) } else if personality @@ -153,118 +149,129 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { )); if rtsim_entity.brain.remembers_character(&tgt_stats.name) { if personality.will_ambush { - "Just follow me a bit more, hehe.".to_string() + format!( + "I'm heading to {}! Want to come along? We'll \ + make great travel buddies, hehe.", + destination_name + ) } else if personality .personality_traits .contains(PersonalityTrait::Extroverted) { - format!( - "Greetings fair {}! It has been far too long \ - since last I saw you. I'm going to {} right \ - now.", - &tgt_stats.name, destination_name - ) - } else if personality - .personality_traits - .contains(PersonalityTrait::Disagreeable) - { - "Oh. It's you again.".to_string() + if personality + .personality_traits + .contains(PersonalityTrait::Extroverted) + { + format!( + "Greetings fair {}! It has been far \ + too long since last I saw you. I'm \ + going to {} right now.", + &tgt_stats.name, destination_name + ) + } else if personality + .personality_traits + .contains(PersonalityTrait::Disagreeable) + { + "Oh. It's you again.".to_string() + } else { + format!( + "Hi again {}! Unfortunately I'm in a \ + hurry right now. See you!", + &tgt_stats.name + ) + } } else { - format!( - "Hi again {}! Unfortunately I'm in a hurry \ - right now. See you!", - &tgt_stats.name - ) + standard_response_msg() } } else { standard_response_msg() + }; + agent_data.chat_npc(msg, event_emitter); + } + } else*/ + if agent.behavior.can_trade(agent_data.alignment.copied(), by) { + if !agent.behavior.is(BehaviorState::TRADING) { + controller.push_initiate_invite(by, InviteKind::Trade); + agent_data.chat_npc( + "npc-speech-merchant_advertisement", + event_emitter, + ); + } else { + let default_msg = "npc-speech-merchant_busy"; + let msg = default_msg/*agent_data.rtsim_entity.map_or(default_msg, |e| { + if e.brain + .personality + .personality_traits + .contains(PersonalityTrait::Disagreeable) + { + "npc-speech-merchant_busy_rude" + } else { + default_msg } - } else { - standard_response_msg() + })*/; + agent_data.chat_npc(msg, event_emitter); + } + } else { + let mut rng = thread_rng(); + /*if let Some(extreme_trait) = + agent_data.rtsim_entity.and_then(|e| { + e.brain.personality.random_chat_trait(&mut rng) + }) + { + let msg = match extreme_trait { + PersonalityTrait::Open => { + "npc-speech-villager_open" + }, + PersonalityTrait::Adventurous => { + "npc-speech-villager_adventurous" + }, + PersonalityTrait::Closed => { + "npc-speech-villager_closed" + }, + PersonalityTrait::Conscientious => { + "npc-speech-villager_conscientious" + }, + PersonalityTrait::Busybody => { + "npc-speech-villager_busybody" + }, + PersonalityTrait::Unconscientious => { + "npc-speech-villager_unconscientious" + }, + PersonalityTrait::Extroverted => { + "npc-speech-villager_extroverted" + }, + PersonalityTrait::Introverted => { + "npc-speech-villager_introverted" + }, + PersonalityTrait::Agreeable => { + "npc-speech-villager_agreeable" + }, + PersonalityTrait::Sociable => { + "npc-speech-villager_sociable" + }, + PersonalityTrait::Disagreeable => { + "npc-speech-villager_disagreeable" + }, + PersonalityTrait::Neurotic => { + "npc-speech-villager_neurotic" + }, + PersonalityTrait::Seeker => { + "npc-speech-villager_seeker" + }, + PersonalityTrait::SadLoner => { + "npc-speech-villager_sad_loner" + }, + PersonalityTrait::Worried => { + "npc-speech-villager_worried" + }, + PersonalityTrait::Stable => { + "npc-speech-villager_stable" + }, }; agent_data.chat_npc(msg, event_emitter); - } else if agent - .behavior - .can_trade(agent_data.alignment.copied(), by) + } else*/ { - if !agent.behavior.is(BehaviorState::TRADING) { - controller.push_initiate_invite(by, InviteKind::Trade); - agent_data.chat_npc( - "npc-speech-merchant_advertisement", - event_emitter, - ); - } else { - let default_msg = "npc-speech-merchant_busy"; - let msg = &bdata.rtsim_entity.map_or(default_msg, |e| { - if e.brain - .personality - .personality_traits - .contains(PersonalityTrait::Disagreeable) - { - "npc-speech-merchant_busy_rude" - } else { - default_msg - } - }); - agent_data.chat_npc(msg, event_emitter); - } - } else { - let mut rng = thread_rng(); - if let Some(extreme_trait) = &bdata.rtsim_entity.and_then(|e| { - e.brain.personality.random_chat_trait(&mut rng) - }) { - let msg = match extreme_trait { - PersonalityTrait::Open => "npc-speech-villager_open", - PersonalityTrait::Adventurous => { - "npc-speech-villager_adventurous" - }, - PersonalityTrait::Closed => { - "npc-speech-villager_closed" - }, - PersonalityTrait::Conscientious => { - "npc-speech-villager_conscientious" - }, - PersonalityTrait::Busybody => { - "npc-speech-villager_busybody" - }, - PersonalityTrait::Unconscientious => { - "npc-speech-villager_unconscientious" - }, - PersonalityTrait::Extroverted => { - "npc-speech-villager_extroverted" - }, - PersonalityTrait::Introverted => { - "npc-speech-villager_introverted" - }, - PersonalityTrait::Agreeable => { - "npc-speech-villager_agreeable" - }, - PersonalityTrait::Sociable => { - "npc-speech-villager_sociable" - }, - PersonalityTrait::Disagreeable => { - "npc-speech-villager_disagreeable" - }, - PersonalityTrait::Neurotic => { - "npc-speech-villager_neurotic" - }, - PersonalityTrait::Seeker => { - "npc-speech-villager_seeker" - }, - PersonalityTrait::SadLoner => { - "npc-speech-villager_sad_loner" - }, - PersonalityTrait::Worried => { - "npc-speech-villager_worried" - }, - PersonalityTrait::Stable => { - "npc-speech-villager_stable" - }, - }; - agent_data.chat_npc(msg, event_emitter); - } else { - agent_data.chat_npc("npc-speech-villager", event_emitter); - } + agent_data.chat_npc("npc-speech-villager", event_emitter); } } }, @@ -295,6 +302,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { } }, Subject::Mood => { + /* if let Some(rtsim_entity) = &bdata.rtsim_entity { if !rtsim_entity.brain.remembers_mood() { // TODO: the following code will need a rework to @@ -325,7 +333,9 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { 2 => agent.rtsim_controller.events.push( RtSimEvent::SetMood(Memory { item: MemoryItem::Mood { - state: MoodState::Bad(MoodContext::GoodWeather), + state: MoodState::Bad( + MoodContext::GoodWeather, + ), }, time_to_forget: read_data.time.0 + 86400.0, }), @@ -340,7 +350,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { }; agent_data.chat_npc(msg, event_emitter); } - } + }*/ }, Subject::Location(location) => { if let Some(tgt_pos) = read_data.positions.get(target) { @@ -750,6 +760,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool { if used { agent.inbox.pop_front(); } + return used; } false } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 3df4ff170e..d7bd86276f 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -10,7 +10,7 @@ use crate::{ chunk_serialize::ChunkSendEntry, client::Client, presence::{Presence, RepositionOnChunkLoad}, - rtsim::RtSim, + rtsim2, settings::Settings, ChunkRequest, Tick, }; @@ -49,6 +49,11 @@ pub type TerrainPersistenceData<'a> = (); pub const SAFE_ZONE_RADIUS: f32 = 200.0; +#[cfg(feature = "worldgen")] +type RtSimData<'a> = WriteExpect<'a, rtsim2::RtSim>; +#[cfg(not(feature = "worldgen"))] +type RtSimData<'a> = (); + /// This system will handle loading generated chunks and unloading /// unneeded chunks. /// 1. Inserts newly generated chunks into the TerrainGrid @@ -73,7 +78,8 @@ impl<'a> System<'a> for Sys { WriteExpect<'a, TerrainGrid>, Write<'a, TerrainChanges>, Write<'a, Vec>, - WriteExpect<'a, RtSim>, + //WriteExpect<'a, RtSim>, + RtSimData<'a>, TerrainPersistenceData<'a>, WriteStorage<'a, Pos>, ReadStorage<'a, Presence>, @@ -105,7 +111,8 @@ impl<'a> System<'a> for Sys { mut terrain, mut terrain_changes, mut chunk_requests, - mut rtsim, + //mut rtsim, + mut rtsim2, mut _terrain_persistence, mut positions, presences, @@ -174,7 +181,8 @@ impl<'a> System<'a> for Sys { terrain_changes.modified_chunks.insert(key); } else { terrain_changes.new_chunks.insert(key); - rtsim.hook_load_chunk(key); + #[cfg(feature = "worldgen")] + rtsim2.hook_load_chunk(key); } // Handle chunk supplement @@ -378,7 +386,8 @@ impl<'a> System<'a> for Sys { // TODO: code duplication for chunk insertion between here and state.rs terrain.remove(key).map(|chunk| { terrain_changes.removed_chunks.insert(key); - rtsim.hook_unload_chunk(key); + #[cfg(feature = "worldgen")] + rtsim2.hook_unload_chunk(key); chunk }) })