mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added rtsim sites
This commit is contained in:
parent
1dc7518200
commit
c856f2625c
@ -11,6 +11,8 @@ use crate::comp::dialogue::MoodState;
|
|||||||
|
|
||||||
slotmap::new_key_type! { pub struct NpcId; }
|
slotmap::new_key_type! { pub struct NpcId; }
|
||||||
|
|
||||||
|
slotmap::new_key_type! { pub struct SiteId; }
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct RtSimEntity(pub NpcId);
|
pub struct RtSimEntity(pub NpcId);
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
pub mod npc;
|
pub mod npc;
|
||||||
|
pub mod site;
|
||||||
pub mod nature;
|
pub mod nature;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
npc::{Npc, NpcId, Npcs},
|
npc::{Npc, NpcId, Npcs},
|
||||||
|
site::{Site, SiteId, Sites},
|
||||||
nature::Nature,
|
nature::Nature,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ pub enum Actor {
|
|||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub nature: Nature,
|
pub nature: Nature,
|
||||||
pub npcs: Npcs,
|
pub npcs: Npcs,
|
||||||
|
pub sites: Sites,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ReadError = rmp_serde::decode::Error;
|
pub type ReadError = rmp_serde::decode::Error;
|
||||||
@ -40,13 +43,6 @@ impl Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn rugged_ser_enum_map<K: EnumArray<V> + Serialize, V: PartialEq + Default + Serialize, S: ser::Serializer>(map: &EnumMap<K, V>, mut ser: S) -> Result<S::Ok, S::Error> {
|
|
||||||
// ser.collect_map(map
|
|
||||||
// .iter()
|
|
||||||
// .filter(|(k, v)| v != &&V::default())
|
|
||||||
// .map(|(k, v)| (k, v)))
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn rugged_ser_enum_map<
|
fn rugged_ser_enum_map<
|
||||||
K: EnumArray<V> + Serialize,
|
K: EnumArray<V> + Serialize,
|
||||||
V: From<i16> + PartialEq + Serialize,
|
V: From<i16> + PartialEq + Serialize,
|
||||||
|
@ -3,21 +3,47 @@ use serde::{Serialize, Deserialize};
|
|||||||
use slotmap::HopSlotMap;
|
use slotmap::HopSlotMap;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use common::uid::Uid;
|
use common::{
|
||||||
|
uid::Uid,
|
||||||
|
store::Id,
|
||||||
|
rtsim::SiteId,
|
||||||
|
};
|
||||||
pub use common::rtsim::NpcId;
|
pub use common::rtsim::NpcId;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Npc {
|
pub struct Npc {
|
||||||
pub wpos: Vec3<f32>,
|
// Persisted state
|
||||||
|
|
||||||
|
/// Represents the location of the NPC.
|
||||||
|
pub loc: NpcLoc,
|
||||||
|
|
||||||
|
// Unpersisted state
|
||||||
|
|
||||||
|
/// The position of the NPC in the world. Note that this is derived from [`Npc::loc`] and cannot be updated manually
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
wpos: Vec3<f32>,
|
||||||
|
/// Whether the NPC is in simulated or loaded mode (when rtsim is run on the server, loaded corresponds to being
|
||||||
|
/// within a loaded chunk). When in loaded mode, the interactions of the NPC should not be simulated but should
|
||||||
|
/// instead be derived from the game.
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub mode: NpcMode,
|
pub mode: NpcMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Npc {
|
impl Npc {
|
||||||
pub fn at(wpos: Vec3<f32>) -> Self {
|
pub fn new(loc: NpcLoc) -> Self {
|
||||||
Self { wpos, mode: NpcMode::Simulated }
|
Self {
|
||||||
|
loc,
|
||||||
|
wpos: Vec3::zero(),
|
||||||
|
mode: NpcMode::Simulated,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wpos(&self) -> Vec3<f32> { self.wpos }
|
||||||
|
|
||||||
|
/// You almost certainly *DO NOT* want to use this method.
|
||||||
|
///
|
||||||
|
/// Update the NPC's wpos as a result of routine NPC simulation derived from its location.
|
||||||
|
pub(crate) fn tick_wpos(&mut self, wpos: Vec3<f32>) { self.wpos = wpos; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
@ -29,13 +55,24 @@ pub enum NpcMode {
|
|||||||
Loaded,
|
Loaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub enum NpcLoc {
|
||||||
|
Wild { wpos: Vec3<f32> },
|
||||||
|
Site { site: SiteId, wpos: Vec3<f32> },
|
||||||
|
Travelling {
|
||||||
|
a: SiteId,
|
||||||
|
b: SiteId,
|
||||||
|
frac: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Npcs {
|
pub struct Npcs {
|
||||||
pub npcs: HopSlotMap<NpcId, Npc>,
|
pub npcs: HopSlotMap<NpcId, Npc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Npcs {
|
impl Npcs {
|
||||||
pub fn spawn(&mut self, npc: Npc) -> NpcId {
|
pub fn create(&mut self, npc: Npc) -> NpcId {
|
||||||
self.npcs.insert(npc)
|
self.npcs.insert(npc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
rtsim/src/data/site.rs
Normal file
47
rtsim/src/data/site.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use slotmap::HopSlotMap;
|
||||||
|
use vek::*;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use common::{
|
||||||
|
uid::Uid,
|
||||||
|
store::Id,
|
||||||
|
};
|
||||||
|
pub use common::rtsim::SiteId;
|
||||||
|
use world::site::Site as WorldSite;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Site {
|
||||||
|
pub wpos: Vec2<i32>,
|
||||||
|
|
||||||
|
/// The site generated during initial worldgen that this site corresponds to.
|
||||||
|
///
|
||||||
|
/// Eventually, rtsim should replace initial worldgen's site system and this will not be necessary.
|
||||||
|
///
|
||||||
|
/// When setting up rtsim state, we try to 'link' these two definitions of a site: but if initial worldgen has
|
||||||
|
/// changed, this might not be possible. We try to delete sites that no longer exist during setup, but this is an
|
||||||
|
/// inherent fallible process. If linking fails, we try to delete the site in rtsim2 in order to avoid an
|
||||||
|
/// 'orphaned' site. (TODO: create new sites for new initial worldgen sites that come into being too).
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
pub world_site: Option<Id<WorldSite>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Sites {
|
||||||
|
pub sites: HopSlotMap<SiteId, Site>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sites {
|
||||||
|
pub fn create(&mut self, site: Site) -> SiteId {
|
||||||
|
self.sites.insert(site)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Sites {
|
||||||
|
type Target = HopSlotMap<SiteId, Site>;
|
||||||
|
fn deref(&self) -> &Self::Target { &self.sites }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Sites {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.sites }
|
||||||
|
}
|
@ -1,8 +1,21 @@
|
|||||||
use common::resources::Time;
|
use common::resources::Time;
|
||||||
|
use world::{World, IndexRef};
|
||||||
|
use super::{Rule, RtState};
|
||||||
|
|
||||||
pub trait Event: Clone + 'static {}
|
pub trait Event: Clone + 'static {}
|
||||||
|
|
||||||
|
pub struct EventCtx<'a, R: Rule, E: Event> {
|
||||||
|
pub state: &'a RtState,
|
||||||
|
pub rule: &'a mut R,
|
||||||
|
pub event: &'a E,
|
||||||
|
pub world: &'a World,
|
||||||
|
pub index: IndexRef<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OnSetup;
|
||||||
|
impl Event for OnSetup {}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OnTick { pub dt: f32 }
|
pub struct OnTick { pub dt: f32 }
|
||||||
|
|
||||||
impl Event for OnTick {}
|
impl Event for OnTick {}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
use crate::data::{Npcs, Npc, Data, Nature};
|
pub mod site;
|
||||||
|
|
||||||
|
use crate::data::{
|
||||||
|
npc::{Npcs, Npc, NpcLoc},
|
||||||
|
site::{Sites, Site},
|
||||||
|
Data,
|
||||||
|
Nature,
|
||||||
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use tracing::info;
|
||||||
use world::{
|
use world::{
|
||||||
site::SiteKind,
|
site::SiteKind,
|
||||||
IndexRef,
|
IndexRef,
|
||||||
@ -8,7 +16,7 @@ use world::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub fn generate(index: IndexRef, world: &World) -> Self {
|
pub fn generate(world: &World, index: IndexRef) -> Self {
|
||||||
let mut seed = [0; 32];
|
let mut seed = [0; 32];
|
||||||
seed.iter_mut().zip(&mut index.seed.to_le_bytes()).for_each(|(dst, src)| *dst = *src);
|
seed.iter_mut().zip(&mut index.seed.to_le_bytes()).for_each(|(dst, src)| *dst = *src);
|
||||||
let mut rng = SmallRng::from_seed(seed);
|
let mut rng = SmallRng::from_seed(seed);
|
||||||
@ -16,25 +24,25 @@ impl Data {
|
|||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
nature: Nature::generate(world),
|
nature: Nature::generate(world),
|
||||||
npcs: Npcs { npcs: Default::default() },
|
npcs: Npcs { npcs: Default::default() },
|
||||||
|
sites: Sites { sites: Default::default() },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (site_id, site) in world
|
// Register sites with rtsim
|
||||||
.civs()
|
for (world_site_id, _) in index
|
||||||
.sites
|
.sites
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(site_id, site)| site.site_tmp.map(|id| (site_id, &index.sites[id])))
|
|
||||||
{
|
{
|
||||||
match &site.kind {
|
let site = Site::generate(world_site_id, world, index);
|
||||||
SiteKind::Refactor(site2) => {
|
this.sites.create(site);
|
||||||
let wpos = site.get_origin()
|
}
|
||||||
.map(|e| e as f32 + 0.5)
|
info!("Registering {} rtsim sites from world sites.", this.sites.len());
|
||||||
.with_z(world.sim().get_alt_approx(site.get_origin()).unwrap_or(0.0));
|
|
||||||
// TODO: Better API
|
// Spawn some test entities at the sites
|
||||||
this.npcs.spawn(Npc::at(wpos));
|
for (site_id, site) in this.sites.iter() {
|
||||||
println!("Spawned rtsim NPC at {:?}", wpos);
|
let wpos = site.wpos.map(|e| e as f32)
|
||||||
}
|
.with_z(world.sim().get_alt_approx(site.wpos).unwrap_or(0.0));
|
||||||
_ => {},
|
this.npcs.create(Npc::new(NpcLoc::Site { site: site_id, wpos }));
|
||||||
}
|
println!("Spawned rtsim NPC at {:?}", wpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
this
|
this
|
||||||
|
25
rtsim/src/gen/site.rs
Normal file
25
rtsim/src/gen/site.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use crate::data::Site;
|
||||||
|
use common::store::Id;
|
||||||
|
use world::{
|
||||||
|
site::Site as WorldSite,
|
||||||
|
World,
|
||||||
|
IndexRef,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Site {
|
||||||
|
pub fn generate(world_site: Id<WorldSite>, world: &World, index: IndexRef) -> Self {
|
||||||
|
// match &world_site.kind {
|
||||||
|
// SiteKind::Refactor(site2) => {
|
||||||
|
// let site = Site::generate(world_site_id, world, index);
|
||||||
|
// println!("Registering rtsim site at {:?}...", site.wpos);
|
||||||
|
// this.sites.create(site);
|
||||||
|
// }
|
||||||
|
// _ => {},
|
||||||
|
// }
|
||||||
|
|
||||||
|
Self {
|
||||||
|
wpos: index.sites.get(world_site).get_origin(),
|
||||||
|
world_site: Some(world_site),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,10 @@ pub mod rule;
|
|||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
data::Data,
|
data::Data,
|
||||||
event::{Event, OnTick},
|
event::{Event, EventCtx, OnTick},
|
||||||
rule::{Rule, RuleError},
|
rule::{Rule, RuleError},
|
||||||
};
|
};
|
||||||
|
use world::{World, IndexRef};
|
||||||
use anymap2::SendSyncAnyMap;
|
use anymap2::SendSyncAnyMap;
|
||||||
use tracing::{info, error};
|
use tracing::{info, error};
|
||||||
use atomic_refcell::AtomicRefCell;
|
use atomic_refcell::AtomicRefCell;
|
||||||
@ -25,7 +26,7 @@ pub struct RtState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RuleState<R> = AtomicRefCell<R>;
|
type RuleState<R> = AtomicRefCell<R>;
|
||||||
type EventHandlersOf<E> = Vec<Box<dyn Fn(&RtState, E) + Send + Sync + 'static>>;
|
type EventHandlersOf<E> = Vec<Box<dyn Fn(&RtState, &World, IndexRef, &E) + Send + Sync + 'static>>;
|
||||||
|
|
||||||
impl RtState {
|
impl RtState {
|
||||||
pub fn new(data: Data) -> Self {
|
pub fn new(data: Data) -> Self {
|
||||||
@ -48,7 +49,8 @@ impl RtState {
|
|||||||
|
|
||||||
fn start_default_rules(&mut self) {
|
fn start_default_rules(&mut self) {
|
||||||
info!("Starting default rtsim rules...");
|
info!("Starting default rtsim rules...");
|
||||||
self.start_rule::<rule::example::RuleState>();
|
self.start_rule::<rule::simulate_npcs::SimulateNpcs>();
|
||||||
|
self.start_rule::<rule::setup::Setup>();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_rule<R: Rule>(&mut self) {
|
pub fn start_rule<R: Rule>(&mut self) {
|
||||||
@ -66,13 +68,19 @@ impl RtState {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(&mut R, &RtState, E) + Send + Sync + 'static) {
|
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(EventCtx<R, E>) + Send + Sync + 'static) {
|
||||||
let f = AtomicRefCell::new(f);
|
let f = AtomicRefCell::new(f);
|
||||||
self.event_handlers
|
self.event_handlers
|
||||||
.entry::<EventHandlersOf<E>>()
|
.entry::<EventHandlersOf<E>>()
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(Box::new(move |rtstate, event| {
|
.push(Box::new(move |state, world, index, event| {
|
||||||
(f.borrow_mut())(&mut rtstate.rule_mut(), rtstate, event)
|
(f.borrow_mut())(EventCtx {
|
||||||
|
state,
|
||||||
|
rule: &mut state.rule_mut(),
|
||||||
|
event,
|
||||||
|
world,
|
||||||
|
index,
|
||||||
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,15 +101,15 @@ impl RtState {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit<E: Event>(&mut self, e: E) {
|
pub fn emit<E: Event>(&mut self, e: E, world: &World, index: IndexRef) {
|
||||||
self.event_handlers
|
self.event_handlers
|
||||||
.get::<EventHandlersOf<E>>()
|
.get::<EventHandlersOf<E>>()
|
||||||
.map(|handlers| handlers
|
.map(|handlers| handlers
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|f| f(self, e.clone())));
|
.for_each(|f| f(self, world, index, &e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, dt: f32) {
|
pub fn tick(&mut self, world: &World, index: IndexRef, dt: f32) {
|
||||||
self.emit(OnTick { dt });
|
self.emit(OnTick { dt }, world, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod example;
|
pub mod setup;
|
||||||
|
pub mod simulate_npcs;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::RtState;
|
use super::RtState;
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
use tracing::info;
|
|
||||||
use crate::{
|
|
||||||
event::OnTick,
|
|
||||||
RtState, Rule, RuleError,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct RuleState;
|
|
||||||
|
|
||||||
impl Rule for RuleState {
|
|
||||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
|
||||||
info!("Hello from example rule!");
|
|
||||||
|
|
||||||
rtstate.bind::<Self, OnTick>(|this, rtstate, event| {
|
|
||||||
// println!("Tick!");
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Self)
|
|
||||||
}
|
|
||||||
}
|
|
48
rtsim/src/rule/setup.rs
Normal file
48
rtsim/src/rule/setup.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use tracing::warn;
|
||||||
|
use crate::{
|
||||||
|
data::Site,
|
||||||
|
event::OnSetup,
|
||||||
|
RtState, Rule, RuleError,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This rule runs at rtsim startup and broadly acts to perform some primitive migration/sanitisation in order to
|
||||||
|
/// ensure that the state of rtsim is mostly sensible.
|
||||||
|
pub struct Setup;
|
||||||
|
|
||||||
|
impl Rule for Setup {
|
||||||
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
|
rtstate.bind::<Self, OnSetup>(|ctx| {
|
||||||
|
// Delete rtsim sites that don't correspond to a world site
|
||||||
|
ctx.state.data_mut().sites.retain(|site_id, site| {
|
||||||
|
if let Some((world_site_id, _)) = ctx.index.sites
|
||||||
|
.iter()
|
||||||
|
.find(|(_, world_site)| world_site.get_origin() == site.wpos)
|
||||||
|
{
|
||||||
|
site.world_site = Some(world_site_id);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
warn!("{:?} is no longer valid because the site it was derived from no longer exists. It will now be deleted.", site_id);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate rtsim sites for world sites that don't have a corresponding rtsim site yet
|
||||||
|
for (world_site_id, _) in ctx.index.sites.iter() {
|
||||||
|
if !ctx.state.data().sites
|
||||||
|
.values()
|
||||||
|
.any(|site| site.world_site.expect("Rtsim site not assigned to world site") == world_site_id)
|
||||||
|
{
|
||||||
|
warn!("{:?} is new and does not have a corresponding rtsim site. One will now be generated afresh.", world_site_id);
|
||||||
|
ctx.state
|
||||||
|
.data_mut()
|
||||||
|
.sites
|
||||||
|
.create(Site::generate(world_site_id, ctx.world, ctx.index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Reassign sites for NPCs if they don't have one
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
25
rtsim/src/rule/simulate_npcs.rs
Normal file
25
rtsim/src/rule/simulate_npcs.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use tracing::info;
|
||||||
|
use crate::{
|
||||||
|
data::npc::NpcLoc,
|
||||||
|
event::OnTick,
|
||||||
|
RtState, Rule, RuleError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SimulateNpcs;
|
||||||
|
|
||||||
|
impl Rule for SimulateNpcs {
|
||||||
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
|
|
||||||
|
rtstate.bind::<Self, OnTick>(|ctx| {
|
||||||
|
for (_, npc) in ctx.state.data_mut().npcs.iter_mut() {
|
||||||
|
npc.tick_wpos(match npc.loc {
|
||||||
|
NpcLoc::Wild { wpos } => wpos,
|
||||||
|
NpcLoc::Site { site, wpos } => wpos,
|
||||||
|
NpcLoc::Travelling { a, b, frac } => todo!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
// rtsim::RtSim,
|
// rtsim::RtSim,
|
||||||
sys::terrain::SAFE_ZONE_RADIUS,
|
sys::terrain::SAFE_ZONE_RADIUS,
|
||||||
|
rtsim2,
|
||||||
Server, SpawnPoint, StateExt,
|
Server, SpawnPoint, StateExt,
|
||||||
};
|
};
|
||||||
use authc::Uuid;
|
use authc::Uuid;
|
||||||
@ -26,7 +27,7 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::{HealthChangeInfo, Outcome},
|
outcome::{HealthChangeInfo, Outcome},
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
// rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
states::utils::{AbilityInfo, StageSection},
|
states::utils::{AbilityInfo, StageSection},
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
@ -519,7 +520,6 @@ 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>()
|
||||||
@ -528,10 +528,9 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
|
|||||||
{
|
{
|
||||||
state
|
state
|
||||||
.ecs()
|
.ecs()
|
||||||
.write_resource::<RtSim>()
|
.write_resource::<rtsim2::RtSim>()
|
||||||
.destroy_entity(rtsim_entity.0);
|
.hook_rtsim_entity_delete(rtsim_entity);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
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");
|
||||||
|
@ -707,7 +707,13 @@ impl Server {
|
|||||||
fn on_block_update(ecs: &specs::World, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
fn on_block_update(ecs: &specs::World, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
||||||
// When a resource block updates, inform rtsim
|
// When a resource block updates, inform rtsim
|
||||||
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() {
|
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() {
|
||||||
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(wpos, old_block, new_block);
|
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(
|
||||||
|
&ecs.read_resource::<Arc<world::World>>(),
|
||||||
|
ecs.read_resource::<Arc<world::IndexOwned>>().as_index_ref(),
|
||||||
|
wpos,
|
||||||
|
old_block,
|
||||||
|
new_block,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ use rtsim2::{
|
|||||||
ReadError,
|
ReadError,
|
||||||
},
|
},
|
||||||
rule::Rule,
|
rule::Rule,
|
||||||
|
event::OnSetup,
|
||||||
RtState,
|
RtState,
|
||||||
};
|
};
|
||||||
use specs::{DispatcherBuilder, WorldExt};
|
use specs::{DispatcherBuilder, WorldExt};
|
||||||
@ -80,7 +81,7 @@ impl RtSim {
|
|||||||
warn!("'RTSIM_NOLOAD' is set, skipping loading of rtsim state (old state will be overwritten).");
|
warn!("'RTSIM_NOLOAD' is set, skipping loading of rtsim state (old state will be overwritten).");
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = Data::generate(index, &world);
|
let data = Data::generate(&world, index);
|
||||||
info!("Rtsim data generated.");
|
info!("Rtsim data generated.");
|
||||||
data
|
data
|
||||||
};
|
};
|
||||||
@ -94,6 +95,8 @@ impl RtSim {
|
|||||||
|
|
||||||
rule::start_rules(&mut this.state);
|
rule::start_rules(&mut this.state);
|
||||||
|
|
||||||
|
this.state.emit(OnSetup, world, index);
|
||||||
|
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +123,8 @@ impl RtSim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_block_update(&mut self, wpos: Vec3<i32>, old: Block, new: Block) {
|
pub fn hook_block_update(&mut self, world: &World, index: IndexRef, wpos: Vec3<i32>, old: Block, new: Block) {
|
||||||
self.state.emit(event::OnBlockChange { wpos, old, new });
|
self.state.emit(event::OnBlockChange { wpos, old, new }, world, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_rtsim_entity_unload(&mut self, entity: RtSimEntity) {
|
pub fn hook_rtsim_entity_unload(&mut self, entity: RtSimEntity) {
|
||||||
@ -130,6 +133,11 @@ impl RtSim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hook_rtsim_entity_delete(&mut self, entity: RtSimEntity) {
|
||||||
|
// TODO: Emit event on deletion to catch death?
|
||||||
|
self.state.data_mut().npcs.remove(entity.0);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
||||||
info!("Saving rtsim data...");
|
info!("Saving rtsim data...");
|
||||||
let file_path = self.file_path.clone();
|
let file_path = self.file_path.clone();
|
||||||
|
@ -5,5 +5,5 @@ use rtsim2::RtState;
|
|||||||
|
|
||||||
pub fn start_rules(rtstate: &mut RtState) {
|
pub fn start_rules(rtstate: &mut RtState) {
|
||||||
info!("Starting server rtsim rules...");
|
info!("Starting server rtsim rules...");
|
||||||
rtstate.start_rule::<deplete_resources::State>();
|
rtstate.start_rule::<deplete_resources::DepleteResources>();
|
||||||
}
|
}
|
||||||
|
@ -9,20 +9,20 @@ use common::{
|
|||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct State;
|
pub struct DepleteResources;
|
||||||
|
|
||||||
impl Rule for State {
|
impl Rule for DepleteResources {
|
||||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
info!("Hello from the resource depletion rule!");
|
info!("Hello from the resource depletion rule!");
|
||||||
|
|
||||||
rtstate.bind::<Self, OnBlockChange>(|this, rtstate, event| {
|
rtstate.bind::<Self, OnBlockChange>(|ctx| {
|
||||||
let key = event.wpos
|
let key = ctx.event.wpos
|
||||||
.xy()
|
.xy()
|
||||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
||||||
if let Some(Some(chunk_state)) = rtstate.resource_mut::<ChunkStates>().0.get(key) {
|
if let Some(Some(chunk_state)) = ctx.state.resource_mut::<ChunkStates>().0.get(key) {
|
||||||
let mut chunk_res = rtstate.data().nature.get_chunk_resources(key);
|
let mut chunk_res = ctx.state.data().nature.get_chunk_resources(key);
|
||||||
// Remove resources
|
// Remove resources
|
||||||
if let Some(res) = event.old.get_rtsim_resource() {
|
if let Some(res) = ctx.event.old.get_rtsim_resource() {
|
||||||
if chunk_state.max_res[res] > 0 {
|
if chunk_state.max_res[res] > 0 {
|
||||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 - 1.0)
|
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 - 1.0)
|
||||||
.round()
|
.round()
|
||||||
@ -30,7 +30,7 @@ impl Rule for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add resources
|
// Add resources
|
||||||
if let Some(res) = event.new.get_rtsim_resource() {
|
if let Some(res) = ctx.event.new.get_rtsim_resource() {
|
||||||
if chunk_state.max_res[res] > 0 {
|
if chunk_state.max_res[res] > 0 {
|
||||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 + 1.0)
|
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 + 1.0)
|
||||||
.round()
|
.round()
|
||||||
@ -38,7 +38,7 @@ impl Rule for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Chunk resources = {:?}", chunk_res);
|
println!("Chunk resources = {:?}", chunk_res);
|
||||||
rtstate.data_mut().nature.set_chunk_resources(key, chunk_res);
|
ctx.state.data_mut().nature.set_chunk_resources(key, chunk_res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut emitter = server_event_bus.emitter();
|
let mut emitter = server_event_bus.emitter();
|
||||||
let rtsim = &mut *rtsim;
|
let rtsim = &mut *rtsim;
|
||||||
|
|
||||||
rtsim.state.tick(dt.0);
|
rtsim.state.tick(&world, index.as_index_ref(), dt.0);
|
||||||
|
|
||||||
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
||||||
rtsim.save(&slow_jobs);
|
rtsim.save(&slow_jobs);
|
||||||
@ -47,7 +47,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
||||||
for (npc_id, npc) in rtsim.state.data_mut().npcs.iter_mut() {
|
for (npc_id, npc) in rtsim.state.data_mut().npcs.iter_mut() {
|
||||||
let chunk = npc.wpos
|
let chunk = npc.wpos()
|
||||||
.xy()
|
.xy()
|
||||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| (e as i32).div_euclid(sz as i32));
|
.map2(TerrainChunk::RECT_SIZE, |e, sz| (e as i32).div_euclid(sz as i32));
|
||||||
|
|
||||||
@ -55,11 +55,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
if matches!(npc.mode, NpcMode::Simulated) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) {
|
if matches!(npc.mode, NpcMode::Simulated) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) {
|
||||||
npc.mode = NpcMode::Loaded;
|
npc.mode = NpcMode::Loaded;
|
||||||
|
|
||||||
println!("Loading in rtsim NPC at {:?}", npc.wpos);
|
|
||||||
|
|
||||||
let body = comp::Body::Object(comp::object::Body::Scarecrow);
|
let body = comp::Body::Object(comp::object::Body::Scarecrow);
|
||||||
emitter.emit(ServerEvent::CreateNpc {
|
emitter.emit(ServerEvent::CreateNpc {
|
||||||
pos: comp::Pos(npc.wpos),
|
pos: comp::Pos(npc.wpos()),
|
||||||
stats: comp::Stats::new("Rtsim NPC".to_string()),
|
stats: comp::Stats::new("Rtsim NPC".to_string()),
|
||||||
skill_set: comp::SkillSet::default(),
|
skill_set: comp::SkillSet::default(),
|
||||||
health: None,
|
health: None,
|
||||||
|
@ -7,7 +7,7 @@ use hashbrown::{HashMap, HashSet};
|
|||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebugShape {
|
pub enum DebugShape {
|
||||||
Line([Vec3<f32>; 2]),
|
Line([Vec3<f32>; 2]),
|
||||||
Cylinder {
|
Cylinder {
|
||||||
@ -261,7 +261,8 @@ pub struct DebugShapeId(pub u64);
|
|||||||
|
|
||||||
pub struct Debug {
|
pub struct Debug {
|
||||||
next_shape_id: DebugShapeId,
|
next_shape_id: DebugShapeId,
|
||||||
pending_shapes: HashMap<DebugShapeId, DebugShape>,
|
shapes: HashMap<DebugShapeId, DebugShape>,
|
||||||
|
pending: HashSet<DebugShapeId>,
|
||||||
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4], [f32; 4])>,
|
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4], [f32; 4])>,
|
||||||
pending_deletes: HashSet<DebugShapeId>,
|
pending_deletes: HashSet<DebugShapeId>,
|
||||||
models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
|
models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
|
||||||
@ -272,7 +273,8 @@ impl Debug {
|
|||||||
pub fn new() -> Debug {
|
pub fn new() -> Debug {
|
||||||
Debug {
|
Debug {
|
||||||
next_shape_id: DebugShapeId(0),
|
next_shape_id: DebugShapeId(0),
|
||||||
pending_shapes: HashMap::new(),
|
shapes: HashMap::new(),
|
||||||
|
pending: HashSet::new(),
|
||||||
pending_locals: HashMap::new(),
|
pending_locals: HashMap::new(),
|
||||||
pending_deletes: HashSet::new(),
|
pending_deletes: HashSet::new(),
|
||||||
models: HashMap::new(),
|
models: HashMap::new(),
|
||||||
@ -286,10 +288,15 @@ impl Debug {
|
|||||||
if matches!(shape, DebugShape::TrainTrack { .. }) {
|
if matches!(shape, DebugShape::TrainTrack { .. }) {
|
||||||
self.casts_shadow.insert(id);
|
self.casts_shadow.insert(id);
|
||||||
}
|
}
|
||||||
self.pending_shapes.insert(id, shape);
|
self.shapes.insert(id, shape);
|
||||||
|
self.pending.insert(id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_shape(&self, id: DebugShapeId) -> Option<&DebugShape> {
|
||||||
|
self.shapes.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) {
|
pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) {
|
||||||
self.pending_locals.insert(id, (pos, color, ori));
|
self.pending_locals.insert(id, (pos, color, ori));
|
||||||
}
|
}
|
||||||
@ -297,19 +304,21 @@ impl Debug {
|
|||||||
pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
|
pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
|
||||||
|
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||||
for (id, shape) in self.pending_shapes.drain() {
|
for id in self.pending.drain() {
|
||||||
if let Some(model) = renderer.create_model(&shape.mesh()) {
|
if let Some(shape) = self.shapes.get(&id) {
|
||||||
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
if let Some(model) = renderer.create_model(&shape.mesh()) {
|
||||||
pos: [0.0; 4],
|
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
||||||
color: [1.0, 0.0, 0.0, 1.0],
|
pos: [0.0; 4],
|
||||||
ori: [0.0, 0.0, 0.0, 1.0],
|
color: [1.0, 0.0, 0.0, 1.0],
|
||||||
}]);
|
ori: [0.0, 0.0, 0.0, 1.0],
|
||||||
self.models.insert(id, (model, locals));
|
}]);
|
||||||
} else {
|
self.models.insert(id, (model, locals));
|
||||||
warn!(
|
} else {
|
||||||
"Failed to create model for debug shape {:?}: {:?}",
|
warn!(
|
||||||
id, shape
|
"Failed to create model for debug shape {:?}: {:?}",
|
||||||
);
|
id, shape
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (id, (pos, color, ori)) in self.pending_locals.drain() {
|
for (id, (pos, color, ori)) in self.pending_locals.drain() {
|
||||||
@ -330,6 +339,7 @@ impl Debug {
|
|||||||
}
|
}
|
||||||
for id in self.pending_deletes.drain() {
|
for id in self.pending_deletes.drain() {
|
||||||
self.models.remove(&id);
|
self.models.remove(&id);
|
||||||
|
self.shapes.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1465,17 +1465,28 @@ impl Scene {
|
|||||||
z_min,
|
z_min,
|
||||||
z_max,
|
z_max,
|
||||||
} => {
|
} => {
|
||||||
|
let scale = scale.map_or(1.0, |s| s.0);
|
||||||
current_entities.insert(entity);
|
current_entities.insert(entity);
|
||||||
let s = scale.map_or(1.0, |sc| sc.0);
|
|
||||||
|
let shape = DebugShape::CapsulePrism {
|
||||||
|
p0: *p0 * scale,
|
||||||
|
p1: *p1 * scale,
|
||||||
|
radius: *radius * scale,
|
||||||
|
height: (*z_max - *z_min) * scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If this shape no longer matches, remove the old one
|
||||||
|
if let Some(shape_id) = hitboxes.get(&entity) {
|
||||||
|
if self.debug.get_shape(*shape_id).map_or(false, |s| s != &shape) {
|
||||||
|
self.debug.remove_shape(*shape_id);
|
||||||
|
hitboxes.remove(&entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
||||||
self.debug.add_shape(DebugShape::CapsulePrism {
|
self.debug.add_shape(shape)
|
||||||
p0: *p0 * s,
|
|
||||||
p1: *p1 * s,
|
|
||||||
radius: *radius * s,
|
|
||||||
height: (*z_max - *z_min) * s,
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0];
|
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min * scale, 0.0];
|
||||||
let color = if group == Some(&comp::group::ENEMY) {
|
let color = if group == Some(&comp::group::ENEMY) {
|
||||||
[1.0, 0.0, 0.0, 0.5]
|
[1.0, 0.0, 0.0, 0.5]
|
||||||
} else if group == Some(&comp::group::NPC) {
|
} else if group == Some(&comp::group::NPC) {
|
||||||
|
@ -1430,6 +1430,7 @@ fn find_site_loc(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Failed to place site {:?}.", site_kind);
|
debug!("Failed to place site {:?}.", site_kind);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user