diff --git a/Cargo.lock b/Cargo.lock index d624eeb828..6777d2e7e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1902,6 +1902,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "shred 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/client/src/lib.rs b/client/src/lib.rs index 4a4dc91de1..7fb35a02c9 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -154,10 +154,11 @@ impl Client { self.state.read_component_cloned::(self.player), self.state.read_component_cloned::(self.player), ) { - self.state.write_component(self.player, comp::phys::Vel(vel.0 + input.move_dir * 2.0 - vel.0.map(|e| e * e.abs() + e) * 0.03)); + self.state.write_component(self.player, comp::Control { + move_dir: input.move_dir, + }); if input.move_dir.magnitude() > 0.01 { - self.state.write_component(self.player, comp::phys::Dir(vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0))); self.state.write_component(self.player, comp::Animation::Run); } else { self.state.write_component(self.player, comp::Animation::Idle); diff --git a/common/Cargo.toml b/common/Cargo.toml index 1b99516d23..e12e94bbab 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -18,3 +18,4 @@ serde = "1.0" serde_derive = "1.0" bincode = "1.0" log = "0.4" +rand = "0.5" diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs new file mode 100644 index 0000000000..4617e78f3c --- /dev/null +++ b/common/src/comp/agent.rs @@ -0,0 +1,28 @@ +use specs::{Component, VecStorage}; +use vek::*; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub enum Agent { + Wanderer(Vec2), +} + +impl Component for Agent { + type Storage = VecStorage; +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct Control { + pub move_dir: Vec2, +} + +impl Default for Control { + fn default() -> Self { + Self { + move_dir: Vec2::zero(), + } + } +} + +impl Component for Control { + type Storage = VecStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 4b12f8becf..c67f27cee2 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,8 +1,10 @@ +pub mod agent; pub mod character; pub mod player; pub mod phys; // Reexports +pub use agent::{Agent, Control}; pub use character::Character; pub use player::Player; pub use character::Animation; diff --git a/common/src/state.rs b/common/src/state.rs index 3a26e6126e..84a6736362 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -101,6 +101,8 @@ impl State { ecs.internal_mut().register::(); ecs.internal_mut().register::(); ecs.internal_mut().register::(); + ecs.internal_mut().register::(); + ecs.internal_mut().register::(); // Register resources used by the ECS ecs.internal_mut().add_resource(TimeOfDay(0.0)); diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs new file mode 100644 index 0000000000..2ae4b5ff2b --- /dev/null +++ b/common/src/sys/agent.rs @@ -0,0 +1,34 @@ +// Library +use specs::{Join, Read, ReadStorage, System, WriteStorage}; +use vek::*; + +// Crate +use crate::comp::{Agent, Control, phys::Pos}; + +// Basic ECS AI agent system +pub struct Sys; + +impl<'a> System<'a> for Sys { + type SystemData = ( + WriteStorage<'a, Agent>, + ReadStorage<'a, Pos>, + WriteStorage<'a, Control>, + ); + + fn run(&mut self, (mut agents, pos, mut controls): Self::SystemData) { + for (mut agent, pos, mut control) in (&mut agents, &pos, &mut controls).join() { + match agent { + Agent::Wanderer(bearing) => { + *bearing += Vec2::new( + rand::random::().fract() - 0.5, + rand::random::().fract() - 0.5, + ) - *bearing * 0.05 - pos.0 * 0.001; + + if bearing.magnitude_squared() != 0.0 { + control.move_dir = bearing.normalized(); + } + }, + } + } + } +} diff --git a/common/src/sys/control.rs b/common/src/sys/control.rs new file mode 100644 index 0000000000..88b91cb3c7 --- /dev/null +++ b/common/src/sys/control.rs @@ -0,0 +1,28 @@ +// Library +use specs::{Join, Read, ReadStorage, System, WriteStorage}; +use vek::*; + +// Crate +use crate::comp::{Control, phys::{Pos, Vel, Dir}}; + +// Basic ECS AI agent system +pub struct Sys; + +impl<'a> System<'a> for Sys { + type SystemData = ( + WriteStorage<'a, Vel>, + WriteStorage<'a, Dir>, + ReadStorage<'a, Control>, + ); + + fn run(&mut self, (mut vels, mut dirs, controls): Self::SystemData) { + for (mut vel, mut dir, control) in (&mut vels, &mut dirs, &controls).join() { + // TODO: Don't hard-code this + vel.0 = vel.0 + control.move_dir * 2.0 - vel.0.map(|e| e * e.abs() + e) * 0.03; + + if control.move_dir.magnitude() > 0.01 { + dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0) ; + } + } + } +} diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index f4e5ea313a..968fe2aa79 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -1,11 +1,17 @@ +pub mod agent; +pub mod control; pub mod phys; // External use specs::DispatcherBuilder; // System names +const AGENT_SYS: &str = "agent_sys"; +const CONTROL_SYS: &str = "control_sys"; const MOVEMENT_SYS: &str = "movement_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(phys::MovementSys, MOVEMENT_SYS, &[]); + dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); + dispatch_builder.add(control::Sys, CONTROL_SYS, &[]); + dispatch_builder.add(phys::Sys, MOVEMENT_SYS, &[]); } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 2cf6c46aa6..ab25dcaf6e 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -8,9 +8,9 @@ use crate::{ }; // Basic ECS physics system -pub struct MovementSys; +pub struct Sys; -impl<'a> System<'a> for MovementSys { +impl<'a> System<'a> for Sys { type SystemData = ( WriteStorage<'a, Pos>, ReadStorage<'a, Vel>, diff --git a/server/src/lib.rs b/server/src/lib.rs index a1eb2fa86c..8b05ec10e3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -13,7 +13,7 @@ use common::{ comp, msg::{ClientMsg, ServerMsg}, net::PostOffice, - state::State, + state::{State, Uid}, terrain::TerrainChunk, }; use specs::{ @@ -52,8 +52,11 @@ impl Server { pub fn new() -> Result { let (chunk_tx, chunk_rx) = mpsc::channel(); - Ok(Self { - state: State::new(), + let mut state = State::new(); + state.ecs_mut().internal_mut().register::(); + + let mut this = Self { + state, world: World::new(), postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?, @@ -65,7 +68,17 @@ impl Server { chunk_tx, chunk_rx, pending_chunks: HashSet::new(), - }) + }; + + for i in 0..4 { + this.create_character(comp::Character::test()) + .with(comp::Agent::Wanderer(Vec2::zero())) + .with(comp::Control::default()) + .with(comp::Animation::Run) + .build(); + } + + Ok(this) } /// Get a reference to the server's game state. @@ -90,6 +103,19 @@ impl Server { &mut self.world } + /// Build a non-player character + #[allow(dead_code)] + pub fn create_character(&mut self, character: comp::Character) -> EcsEntityBuilder { + self.state + .ecs_mut() + .create_entity_synced() + .with(comp::phys::Pos(Vec3::zero())) + .with(comp::phys::Vel(Vec3::zero())) + .with(comp::phys::Dir(Vec3::zero())) + .with(character) + .with(comp::Animation::Idle) + } + /// Execute a single server tick, handle input and update the game state by the given duration #[allow(dead_code)] pub fn tick(&mut self, input: Input, dt: Duration) -> Result, Error> { @@ -219,7 +245,7 @@ impl Server { state.write_component(entity, comp::phys::Dir(Vec3::unit_y())); if let Some(character) = character { state.write_component(entity, character); - + } client.state = ClientState::Connected; @@ -339,7 +365,7 @@ impl Server { }; match force_update { - + Some(_) => self.clients.notify_connected(msg), None => self.clients.notify_connected_except(entity, msg),