Began integrating rtsim2 into server

This commit is contained in:
Joshua Barretto 2022-05-16 20:11:49 +01:00
parent ca80d831ce
commit 0cafafdaa7
24 changed files with 625 additions and 163 deletions

5
Cargo.lock generated
View File

@ -6888,9 +6888,11 @@ dependencies = [
name = "veloren-rtsim" name = "veloren-rtsim"
version = "0.10.0" version = "0.10.0"
dependencies = [ dependencies = [
"ron 0.7.0", "hashbrown 0.12.3",
"ron 0.8.0",
"serde", "serde",
"veloren-common", "veloren-common",
"veloren-world",
] ]
[[package]] [[package]]
@ -6942,6 +6944,7 @@ dependencies = [
"veloren-common-systems", "veloren-common-systems",
"veloren-network", "veloren-network",
"veloren-plugin-api", "veloren-plugin-api",
"veloren-rtsim",
"veloren-server-agent", "veloren-server-agent",
"veloren-world", "veloren-world",
] ]

View File

@ -5,5 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
common = { package = "veloren-common", path = "../common" } 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"] } serde = { version = "1.0.110", features = ["derive"] }
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }

13
rtsim/src/data/actor.rs Normal file
View File

@ -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<ActorId, Actor>,
}

View File

@ -3,6 +3,7 @@ use serde::{
Deserialize, Deserializer, Serialize, Serializer, Deserialize, Deserializer, Serialize, Serializer,
}; };
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct V<T>(pub T); pub struct V<T>(pub T);
impl<T: Serialize> Serialize for V<T> { impl<T: Serialize> Serialize for V<T> {
@ -22,12 +23,12 @@ impl<'de, T: Version> Deserialize<'de> for V<T> {
impl<U, T: Latest<U>> Latest<U> for V<T> { impl<U, T: Latest<U>> Latest<U> for V<T> {
fn to_unversioned(self) -> U { self.0.to_unversioned() } 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<T> { pub trait Latest<T> {
fn to_unversioned(self) -> T; fn to_unversioned(self) -> T;
fn from_unversioned(x: T) -> Self; fn from_unversioned(x: &T) -> Self;
} }
pub trait Version: Sized + DeserializeOwned { pub trait Version: Sized + DeserializeOwned {

View File

@ -1,10 +1,30 @@
pub mod helper; pub mod helper;
pub mod version; 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 { pub struct Data {
world: World, pub nature: Nature,
pub actors: Actors,
}
impl Data {
pub fn from_reader<R: Read>(reader: R) -> SpannedResult<Self> {
ron::de::from_reader(reader).map(version::LatestData::to_unversioned)
}
pub fn write_to<W: Write>(&self, writer: W) -> Result<(), ron::Error> {
ron::ser::to_writer(writer, &version::LatestData::from_unversioned(self))
}
} }

1
rtsim/src/data/nature.rs Normal file
View File

@ -0,0 +1 @@
pub struct Nature {}

View File

@ -0,0 +1,85 @@
use super::*;
use crate::data::{Actor, ActorId, Actors};
use hashbrown::HashMap;
// ActorId
impl Latest<ActorId> 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<Actor> 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<Actors> 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<ActorIdV0>, V<ActorV0>>,
}
impl Version for ActorsV0 {
type Prev = Bottom;
fn migrate(x: Self::Prev) -> Self { match x {} }
}

View File

@ -15,11 +15,17 @@
// conclusion that there's just no way to add the feature you want to add // 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. // 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 // Please note that in *very specific* cases, it is possible to make a change to
// module, or submodules. // 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 // 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 // 2) Rename the duplicated version, incrementing the version number (i.e: V0
// becomes V1). // becomes V1).
@ -38,7 +44,8 @@
// The *golden rule* is that, once merged to master, an old version's type must // The *golden rule* is that, once merged to master, an old version's type must
// not be changed! // not be changed!
pub mod world; pub mod actor;
pub mod nature;
use super::{ use super::{
helper::{Bottom, Latest, Version, V}, helper::{Bottom, Latest, Version, V},
@ -46,23 +53,28 @@ use super::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
impl Latest<Data> for DataV0 { pub type LatestData = DataV0;
impl Latest<Data> for LatestData {
fn to_unversioned(self) -> Data { fn to_unversioned(self) -> Data {
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 { Self {
world: Latest::from_unversioned(data.world), nature: Latest::from_unversioned(&data.nature),
actors: Latest::from_unversioned(&data.actors),
} }
} }
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct DataV0 { pub struct DataV0 {
world: V<world::WorldV0>, nature: V<nature::NatureV0>,
actors: V<actor::ActorsV0>,
} }
impl Version for DataV0 { impl Version for DataV0 {

View File

@ -0,0 +1,17 @@
use super::*;
use crate::data::Nature;
impl Latest<crate::data::Nature> 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 {} }
}

View File

@ -1,17 +0,0 @@
use super::*;
use crate::data::World;
impl Latest<crate::data::World> 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 {} }
}

View File

@ -1 +0,0 @@
pub struct World {}

14
rtsim/src/gen/mod.rs Normal file
View File

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

View File

@ -1,7 +1,10 @@
pub mod data; pub mod data;
pub mod gen;
/* use self::data::Data;
pub struct RtState<'a> { use std::sync::Arc;
use world::World;
pub struct RtState {
pub data: Data,
} }
*/

View File

@ -23,6 +23,7 @@ common-state = { package = "veloren-common-state", path = "../common/state" }
common-systems = { package = "veloren-common-systems", path = "../common/systems" } common-systems = { package = "veloren-common-systems", path = "../common/systems" }
common-net = { package = "veloren-common-net", path = "../common/net" } common-net = { package = "veloren-common-net", path = "../common/net" }
world = { package = "veloren-world", path = "../world" } 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 } network = { package = "veloren-network", path = "../network", features = ["metrics", "compression", "quic"], default-features = false }
server-agent = {package = "veloren-server-agent", path = "agent"} server-agent = {package = "veloren-server-agent", path = "agent"}

View File

@ -236,7 +236,7 @@ pub struct ReadData<'a> {
pub light_emitter: ReadStorage<'a, LightEmitter>, pub light_emitter: ReadStorage<'a, LightEmitter>,
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
pub world: ReadExpect<'a, Arc<world::World>>, pub world: ReadExpect<'a, Arc<world::World>>,
pub rtsim_entities: ReadStorage<'a, RtSimEntity>, // pub rtsim_entities: ReadStorage<'a, RtSimEntity>,
pub buffs: ReadStorage<'a, Buffs>, pub buffs: ReadStorage<'a, Buffs>,
pub combos: ReadStorage<'a, Combo>, pub combos: ReadStorage<'a, Combo>,
pub active_abilities: ReadStorage<'a, ActiveAbilities>, pub active_abilities: ReadStorage<'a, ActiveAbilities>,

View File

@ -9,6 +9,7 @@ pub enum Error {
StreamErr(StreamError), StreamErr(StreamError),
DatabaseErr(rusqlite::Error), DatabaseErr(rusqlite::Error),
PersistenceErr(PersistenceError), PersistenceErr(PersistenceError),
RtsimError(ron::Error),
Other(String), Other(String),
} }
@ -41,6 +42,7 @@ impl Display for Error {
Self::StreamErr(err) => write!(f, "Stream Error: {}", err), Self::StreamErr(err) => write!(f, "Stream Error: {}", err),
Self::DatabaseErr(err) => write!(f, "Database Error: {}", err), Self::DatabaseErr(err) => write!(f, "Database Error: {}", err),
Self::PersistenceErr(err) => write!(f, "Persistence 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), Self::Other(err) => write!(f, "Error: {}", err),
} }
} }

View File

@ -519,6 +519,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
} }
if should_delete { if should_delete {
/*
if let Some(rtsim_entity) = state if let Some(rtsim_entity) = state
.ecs() .ecs()
.read_storage::<RtSimEntity>() .read_storage::<RtSimEntity>()
@ -530,6 +531,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
.write_resource::<RtSim>() .write_resource::<RtSim>()
.destroy_entity(rtsim_entity.0); .destroy_entity(rtsim_entity.0);
} }
*/
if let Err(e) = state.delete_entity_recorded(entity) { if let Err(e) = state.delete_entity_recorded(entity) {
error!(?e, ?entity, "Failed to delete destroyed entity"); error!(?e, ?entity, "Failed to delete destroyed entity");

View File

@ -30,6 +30,7 @@ pub mod persistence;
mod pet; mod pet;
pub mod presence; pub mod presence;
pub mod rtsim; pub mod rtsim;
pub mod rtsim2;
pub mod settings; pub mod settings;
pub mod state_ext; pub mod state_ext;
pub mod sys; pub mod sys;
@ -548,6 +549,7 @@ impl Server {
let connection_handler = ConnectionHandler::new(network, &runtime); let connection_handler = ConnectionHandler::new(network, &runtime);
// Initiate real-time world simulation // Initiate real-time world simulation
/*
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
{ {
rtsim::init(&mut state, &world, index.as_index_ref()); rtsim::init(&mut state, &world, index.as_index_ref());
@ -555,6 +557,20 @@ impl Server {
} }
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]
rtsim::init(&mut state); 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 { let server_constants = ServerConstants {
day_cycle_coefficient: 1440.0 / settings.day_length, day_cycle_coefficient: 1440.0 / settings.day_length,
@ -694,11 +710,18 @@ impl Server {
add_local_systems(dispatcher_builder); add_local_systems(dispatcher_builder);
sys::msg::add_server_systems(dispatcher_builder); sys::msg::add_server_systems(dispatcher_builder);
sys::add_server_systems(dispatcher_builder); sys::add_server_systems(dispatcher_builder);
/*
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
{ {
rtsim::add_server_systems(dispatcher_builder); rtsim::add_server_systems(dispatcher_builder);
weather::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, false,
Some(&mut state_tick_metrics), Some(&mut state_tick_metrics),
@ -805,6 +828,7 @@ impl Server {
}; };
for entity in to_delete { for entity in to_delete {
/*
// Assimilate entities that are part of the real-time world simulation // Assimilate entities that are part of the real-time world simulation
if let Some(rtsim_entity) = self if let Some(rtsim_entity) = self
.state .state
@ -818,6 +842,7 @@ impl Server {
.write_resource::<RtSim>() .write_resource::<RtSim>()
.assimilate_entity(rtsim_entity.0); .assimilate_entity(rtsim_entity.0);
} }
*/
if let Err(e) = self.state.delete_entity_recorded(entity) { if let Err(e) = self.state.delete_entity_recorded(entity) {
error!(?e, "Failed to delete agent outside the terrain"); error!(?e, "Failed to delete agent outside the terrain");

70
server/src/rtsim2/mod.rs Normal file
View File

@ -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<bool>, // true = loaded
state: RtState,
}
impl RtSim {
pub fn new(world: &World, data_dir: PathBuf) -> Result<Self, ron::Error> {
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<i32>) {
if let Some(is_loaded) = self.chunk_states.get_mut(key) {
*is_loaded = true;
}
}
pub fn hook_unload_chunk(&mut self, key: Vec2<i32>) {
if let Some(is_loaded) = self.chunk_states.get_mut(key) {
*is_loaded = false;
}
}
}
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch::<tick::Sys>(dispatch_builder, &[]);
}

179
server/src/rtsim2/tick.rs Normal file
View File

@ -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<ServerEvent>>,
WriteExpect<'a, RtSim>,
ReadExpect<'a, Arc<world::World>>,
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<Self>,
(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();
});
}
*/
}
}

View File

@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys {
Read<'a, EventBus<ServerEvent>>, Read<'a, EventBus<ServerEvent>>,
WriteStorage<'a, Agent>, WriteStorage<'a, Agent>,
WriteStorage<'a, Controller>, WriteStorage<'a, Controller>,
WriteExpect<'a, RtSim>, //WriteExpect<'a, RtSim>,
); );
const NAME: &'static str = "agent"; const NAME: &'static str = "agent";
@ -41,9 +41,9 @@ impl<'a> System<'a> for Sys {
fn run( fn run(
job: &mut Job<Self>, job: &mut Job<Self>,
(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); 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()), can_fly: body.map_or(false, |b| b.fly_thrust().is_some()),
}; };
let health_fraction = health.map_or(1.0, Health::fraction); let health_fraction = health.map_or(1.0, Health::fraction);
/*
let rtsim_entity = read_data let rtsim_entity = read_data
.rtsim_entities .rtsim_entities
.get(entity) .get(entity)
.and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0)); .and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0));
*/
if traversal_config.can_fly && matches!(body, Some(Body::Ship(_))) { if traversal_config.can_fly && matches!(body, Some(Body::Ship(_))) {
// hack (kinda): Never turn off flight airships // hack (kinda): Never turn off flight airships
@ -226,7 +228,7 @@ impl<'a> System<'a> for Sys {
// inputs. // inputs.
let mut behavior_data = BehaviorData { let mut behavior_data = BehaviorData {
agent, agent,
rtsim_entity, // rtsim_entity,
agent_data: data, agent_data: data,
read_data: &read_data, read_data: &read_data,
event_emitter: &mut event_emitter, 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()); 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() { for (agent, rtsim_entity) in (&mut agents, &read_data.rtsim_entities).join() {
// Entity must be loaded in as it has an agent component :) // Entity must be loaded in as it has an agent component :)
// React to all events in the controller // React to all events in the controller
@ -258,5 +261,6 @@ impl<'a> System<'a> for Sys {
} }
} }
} }
*/
} }
} }

View File

@ -41,7 +41,7 @@ pub struct BehaviorData<'a, 'b, 'c> {
pub agent: &'a mut Agent, pub agent: &'a mut Agent,
pub agent_data: AgentData<'a>, pub agent_data: AgentData<'a>,
// TODO: Move rtsim back into AgentData after rtsim2 when it has a separate crate // 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 read_data: &'a ReadData<'a>,
pub event_emitter: &'a mut Emitter<'c, ServerEvent>, pub event_emitter: &'a mut Emitter<'c, ServerEvent>,
pub controller: &'a mut Controller, 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 // Remember this attack if we're an RtSim entity
/*
if let Some(attacker_stats) = if let Some(attacker_stats) =
bdata.rtsim_entity.and(bdata.read_data.stats.get(attacker)) bdata.rtsim_entity.and(bdata.read_data.stats.get(attacker))
{ {
@ -284,6 +285,7 @@ fn target_if_attacked(bdata: &mut BehaviorData) -> bool {
.agent .agent
.add_fight_to_memory(&attacker_stats.name, bdata.read_data.time.0); .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 let Some(tgt_health) = bdata.read_data.healths.get(target) {
// If target is dead, forget them // If target is dead, forget them
if tgt_health.is_dead { if tgt_health.is_dead {
/*
if let Some(tgt_stats) = bdata.rtsim_entity.and(bdata.read_data.stats.get(target)) { if let Some(tgt_stats) = bdata.rtsim_entity.and(bdata.read_data.stats.get(target)) {
bdata.agent.forget_enemy(&tgt_stats.name); bdata.agent.forget_enemy(&tgt_stats.name);
} }
*/
bdata.agent.target = None; bdata.agent.target = None;
return true; return true;
} }
@ -496,7 +500,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
bdata.controller, bdata.controller,
bdata.read_data, bdata.read_data,
bdata.event_emitter, bdata.event_emitter,
will_ambush(bdata.rtsim_entity, &bdata.agent_data), will_ambush(/* bdata.rtsim_entity */ None, &bdata.agent_data),
); );
} else { } else {
bdata.agent_data.handle_sounds_heard( bdata.agent_data.handle_sounds_heard(
@ -639,7 +643,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
let BehaviorData { let BehaviorData {
agent, agent,
agent_data, agent_data,
rtsim_entity, // rtsim_entity,
read_data, read_data,
event_emitter, event_emitter,
controller, controller,
@ -746,7 +750,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
controller, controller,
read_data, read_data,
event_emitter, 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, read_data,
event_emitter, event_emitter,
rng, 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 { fn will_ambush(rtsim_entity: Option<&RtSimEntity>, agent_data: &AgentData) -> bool {
// TODO: implement for rtsim2
agent_data agent_data
.health .health
.map_or(false, |h| h.current() / h.maximum() > 0.7) .map_or(false, |h| h.current() / h.maximum() > 0.7)
@ -786,6 +791,7 @@ fn remembers_fight_with(
read_data: &ReadData, read_data: &ReadData,
other: EcsEntity, other: EcsEntity,
) -> bool { ) -> bool {
// TODO: implement for rtsim2
let name = || read_data.stats.get(other).map(|stats| stats.name.clone()); let name = || read_data.stats.get(other).map(|stats| stats.name.clone());
rtsim_entity.map_or(false, |rtsim_entity| { rtsim_entity.map_or(false, |rtsim_entity| {

View File

@ -108,21 +108,17 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
match subject { match subject {
Subject::Regular => { Subject::Regular => {
/*
if let Some(rtsim_entity) = &bdata.rtsim_entity { if let Some(rtsim_entity) = &bdata.rtsim_entity {
if matches!(rtsim_entity.kind, RtSimEntityKind::Prisoner) { if matches!(rtsim_entity.kind, RtSimEntityKind::Prisoner) {
agent_data.chat_npc("npc-speech-prisoner", event_emitter); agent_data.chat_npc("npc-speech-prisoner", event_emitter);
} else if let ( } else if let Some((_travel_to, destination_name) = &agent.rtsim_controller.travel_to {
Some((_travel_to, destination_name)),
Some(rtsim_entity),
) =
(&agent.rtsim_controller.travel_to, &&bdata.rtsim_entity)
{
let personality = &rtsim_entity.brain.personality; let personality = &rtsim_entity.brain.personality;
let standard_response_msg = || -> String { let standard_response_msg = || -> String {
if personality.will_ambush { if personality.will_ambush {
format!( format!(
"I'm heading to {}! Want to come along? We'll \ "I'm heading to {}! Want to come along? We'll make \
make great travel buddies, hehe.", great travel buddies, hehe.",
destination_name destination_name
) )
} else if personality } 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 rtsim_entity.brain.remembers_character(&tgt_stats.name) {
if personality.will_ambush { 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 } else if personality
.personality_traits .personality_traits
.contains(PersonalityTrait::Extroverted) .contains(PersonalityTrait::Extroverted)
{ {
format!( if personality
"Greetings fair {}! It has been far too long \ .personality_traits
since last I saw you. I'm going to {} right \ .contains(PersonalityTrait::Extroverted)
now.", {
&tgt_stats.name, destination_name format!(
) "Greetings fair {}! It has been far \
} else if personality too long since last I saw you. I'm \
.personality_traits going to {} right now.",
.contains(PersonalityTrait::Disagreeable) &tgt_stats.name, destination_name
{ )
"Oh. It's you again.".to_string() } 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 { } else {
format!( standard_response_msg()
"Hi again {}! Unfortunately I'm in a hurry \
right now. See you!",
&tgt_stats.name
)
} }
} else { } else {
standard_response_msg() 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); agent_data.chat_npc(msg, event_emitter);
} else if agent } else*/
.behavior
.can_trade(agent_data.alignment.copied(), by)
{ {
if !agent.behavior.is(BehaviorState::TRADING) { agent_data.chat_npc("npc-speech-villager", event_emitter);
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);
}
} }
} }
}, },
@ -295,6 +302,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
} }
}, },
Subject::Mood => { Subject::Mood => {
/*
if let Some(rtsim_entity) = &bdata.rtsim_entity { if let Some(rtsim_entity) = &bdata.rtsim_entity {
if !rtsim_entity.brain.remembers_mood() { if !rtsim_entity.brain.remembers_mood() {
// TODO: the following code will need a rework to // 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( 2 => agent.rtsim_controller.events.push(
RtSimEvent::SetMood(Memory { RtSimEvent::SetMood(Memory {
item: MemoryItem::Mood { item: MemoryItem::Mood {
state: MoodState::Bad(MoodContext::GoodWeather), state: MoodState::Bad(
MoodContext::GoodWeather,
),
}, },
time_to_forget: read_data.time.0 + 86400.0, 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); agent_data.chat_npc(msg, event_emitter);
} }
} }*/
}, },
Subject::Location(location) => { Subject::Location(location) => {
if let Some(tgt_pos) = read_data.positions.get(target) { 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 { if used {
agent.inbox.pop_front(); agent.inbox.pop_front();
} }
return used;
} }
false false
} }

View File

@ -10,7 +10,7 @@ use crate::{
chunk_serialize::ChunkSendEntry, chunk_serialize::ChunkSendEntry,
client::Client, client::Client,
presence::{Presence, RepositionOnChunkLoad}, presence::{Presence, RepositionOnChunkLoad},
rtsim::RtSim, rtsim2,
settings::Settings, settings::Settings,
ChunkRequest, Tick, ChunkRequest, Tick,
}; };
@ -49,6 +49,11 @@ pub type TerrainPersistenceData<'a> = ();
pub const SAFE_ZONE_RADIUS: f32 = 200.0; 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 /// This system will handle loading generated chunks and unloading
/// unneeded chunks. /// unneeded chunks.
/// 1. Inserts newly generated chunks into the TerrainGrid /// 1. Inserts newly generated chunks into the TerrainGrid
@ -73,7 +78,8 @@ impl<'a> System<'a> for Sys {
WriteExpect<'a, TerrainGrid>, WriteExpect<'a, TerrainGrid>,
Write<'a, TerrainChanges>, Write<'a, TerrainChanges>,
Write<'a, Vec<ChunkRequest>>, Write<'a, Vec<ChunkRequest>>,
WriteExpect<'a, RtSim>, //WriteExpect<'a, RtSim>,
RtSimData<'a>,
TerrainPersistenceData<'a>, TerrainPersistenceData<'a>,
WriteStorage<'a, Pos>, WriteStorage<'a, Pos>,
ReadStorage<'a, Presence>, ReadStorage<'a, Presence>,
@ -105,7 +111,8 @@ impl<'a> System<'a> for Sys {
mut terrain, mut terrain,
mut terrain_changes, mut terrain_changes,
mut chunk_requests, mut chunk_requests,
mut rtsim, //mut rtsim,
mut rtsim2,
mut _terrain_persistence, mut _terrain_persistence,
mut positions, mut positions,
presences, presences,
@ -174,7 +181,8 @@ impl<'a> System<'a> for Sys {
terrain_changes.modified_chunks.insert(key); terrain_changes.modified_chunks.insert(key);
} else { } else {
terrain_changes.new_chunks.insert(key); terrain_changes.new_chunks.insert(key);
rtsim.hook_load_chunk(key); #[cfg(feature = "worldgen")]
rtsim2.hook_load_chunk(key);
} }
// Handle chunk supplement // Handle chunk supplement
@ -378,7 +386,8 @@ impl<'a> System<'a> for Sys {
// TODO: code duplication for chunk insertion between here and state.rs // TODO: code duplication for chunk insertion between here and state.rs
terrain.remove(key).map(|chunk| { terrain.remove(key).map(|chunk| {
terrain_changes.removed_chunks.insert(key); terrain_changes.removed_chunks.insert(key);
rtsim.hook_unload_chunk(key); #[cfg(feature = "worldgen")]
rtsim2.hook_unload_chunk(key);
chunk chunk
}) })
}) })