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"
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",
]

View File

@ -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"] }

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,
};
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct V<T>(pub 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> {
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> {
fn to_unversioned(self) -> T;
fn from_unversioned(x: T) -> Self;
fn from_unversioned(x: &T) -> Self;
}
pub trait Version: Sized + DeserializeOwned {

View File

@ -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<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,8 +15,14 @@
// 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).
@ -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<Data> for DataV0 {
pub type LatestData = DataV0;
impl Latest<Data> 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<world::WorldV0>,
nature: V<nature::NatureV0>,
actors: V<actor::ActorsV0>,
}
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 gen;
/*
pub struct RtState<'a> {
use self::data::Data;
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-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"}

View File

@ -236,7 +236,7 @@ pub struct ReadData<'a> {
pub light_emitter: ReadStorage<'a, LightEmitter>,
#[cfg(feature = "worldgen")]
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 combos: ReadStorage<'a, Combo>,
pub active_abilities: ReadStorage<'a, ActiveAbilities>,

View File

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

View File

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

View File

@ -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::<RtSim>()
.assimilate_entity(rtsim_entity.0);
}
*/
if let Err(e) = self.state.delete_entity_recorded(entity) {
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>>,
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<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);
(
@ -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 {
}
}
}
*/
}
}

View File

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

View File

@ -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,15 +149,23 @@ 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)
{
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.",
"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
@ -171,8 +175,8 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
"Oh. It's you again.".to_string()
} else {
format!(
"Hi again {}! Unfortunately I'm in a hurry \
right now. See you!",
"Hi again {}! Unfortunately I'm in a \
hurry right now. See you!",
&tgt_stats.name
)
}
@ -183,10 +187,9 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
standard_response_msg()
};
agent_data.chat_npc(msg, event_emitter);
} else if agent
.behavior
.can_trade(agent_data.alignment.copied(), by)
{
}
} 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(
@ -195,7 +198,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
);
} else {
let default_msg = "npc-speech-merchant_busy";
let msg = &bdata.rtsim_entity.map_or(default_msg, |e| {
let msg = default_msg/*agent_data.rtsim_entity.map_or(default_msg, |e| {
if e.brain
.personality
.personality_traits
@ -205,16 +208,20 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
} 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| {
/*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::Open => {
"npc-speech-villager_open"
},
PersonalityTrait::Adventurous => {
"npc-speech-villager_adventurous"
},
@ -262,11 +269,11 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
},
};
agent_data.chat_npc(msg, event_emitter);
} else {
} else*/
{
agent_data.chat_npc("npc-speech-villager", event_emitter);
}
}
}
},
Subject::Trade => {
if agent.behavior.can_trade(agent_data.alignment.copied(), by) {
@ -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
}

View File

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