Merge branch 'timo-dont-send-nothing-changed' into 'master'

Don't send empty physics updates and fix commands

See merge request veloren/veloren!380
This commit is contained in:
Joshua Barretto 2019-07-31 09:15:05 +00:00
commit 35bf253081
11 changed files with 355 additions and 277 deletions

View File

@ -398,24 +398,27 @@ impl Client {
ServerMsg::EcsSync(sync_package) => {
self.state.ecs_mut().sync_with_package(sync_package)
}
ServerMsg::EntityPhysics {
ServerMsg::EntityPos { entity, pos } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, pos);
}
}
ServerMsg::EntityVel { entity, vel } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, vel);
}
}
ServerMsg::EntityOri { entity, ori } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, ori);
}
}
ServerMsg::EntityActionState {
entity,
pos,
vel,
ori,
action_state,
} => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, pos);
if let Some(v) = vel {
self.state.write_component(entity, v);
};
if let Some(o) = ori {
self.state.write_component(entity, o);
};
if let Some(a_s) = action_state {
self.state.write_component(entity, a_s);
}
self.state.write_component(entity, action_state);
}
}
ServerMsg::TerrainChunkUpdate { key, chunk } => {

View File

@ -1,6 +1,6 @@
use specs::{Component, FlaggedStorage, VecStorage};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct ActionState {
pub moving: bool,
pub on_ground: bool,

15
common/src/comp/last.rs Normal file
View File

@ -0,0 +1,15 @@
use specs::{Component, VecStorage};
use std::{fmt::Debug, marker::Send, ops::Deref};
#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize)]
pub struct Last<C: Component + PartialEq>(pub C);
impl<C: Component + Send + Sync + PartialEq> Component for Last<C> {
type Storage = VecStorage<Self>;
}
impl<C: Component + PartialEq> PartialEq<C> for Last<C> {
fn eq(&self, other: &C) -> bool {
self.0 == *other
}
}

View File

@ -5,6 +5,7 @@ mod body;
mod controller;
mod inputs;
mod inventory;
mod last;
mod phys;
mod player;
mod stats;
@ -20,6 +21,7 @@ pub use inputs::{
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
};
pub use inventory::{item, Inventory};
pub use last::Last;
pub use phys::{ForceUpdate, Ori, Pos, Vel};
pub use player::Player;
pub use stats::{Dying, Exp, HealthSource, Level, Stats};

View File

@ -2,7 +2,7 @@ use specs::{Component, NullStorage, VecStorage};
use vek::*;
// Position
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Pos(pub Vec3<f32>);
impl Component for Pos {
@ -10,7 +10,7 @@ impl Component for Pos {
}
// Velocity
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Vel(pub Vec3<f32>);
impl Component for Vel {
@ -18,7 +18,7 @@ impl Component for Vel {
}
// Orientation
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Ori(pub Vec3<f32>);
impl Component for Ori {
@ -26,7 +26,7 @@ impl Component for Ori {
}
// ForceUpdate
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ForceUpdate;
impl Component for ForceUpdate {

View File

@ -39,12 +39,21 @@ pub enum ServerMsg {
},
SetPlayerEntity(u64),
EcsSync(sphynx::SyncPackage<EcsCompPacket, EcsResPacket>),
EntityPhysics {
EntityPos {
entity: u64,
pos: comp::Pos,
vel: Option<comp::Vel>,
ori: Option<comp::Ori>,
action_state: Option<comp::ActionState>,
},
EntityVel {
entity: u64,
vel: comp::Vel,
},
EntityOri {
entity: u64,
ori: comp::Ori,
},
EntityActionState {
entity: u64,
action_state: comp::ActionState,
},
TerrainChunkUpdate {
key: Vec2<i32>,

View File

@ -190,7 +190,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
}
// Try getting messages from the send channel.
for _ in 0..100 {
for _ in 0..1000 {
match send_rx.try_recv() {
Ok(send_msg) => {
// Serialize message
@ -225,7 +225,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
}
// Try sending bytes through the TCP stream.
for _ in 0..100 {
for _ in 0..1000 {
match outgoing_chunks.pop_front() {
Some(mut chunk) => match stream.write(&chunk) {
Ok(n) if n == chunk.len() => {}
@ -249,7 +249,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
}
// Try receiving bytes from the TCP stream.
for _ in 0..100 {
for _ in 0..1000 {
let mut buf = [0; 4096];
match stream.read(&mut buf) {
@ -265,7 +265,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
}
// Try turning bytes into messages.
for _ in 0..100 {
for _ in 0..1000 {
match incoming_buf.get(0..9) {
Some(len_bytes) => {
let len =

View File

@ -125,36 +125,45 @@ impl State {
// Create a new Sphynx ECS world.
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
// Register server->client synced components.
// Register server -> all clients synced components.
ecs.register_synced::<comp::Body>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::CanBuild>();
ecs.register_synced::<comp::LightEmitter>();
// Register components synced by other means
// Register components send from clients -> server
ecs.register::<comp::Controller>();
// Register components send directly from server -> all but one client
ecs.register::<comp::ActionState>();
// Register components synced from client -> server -> all other clients
ecs.register::<comp::Pos>();
ecs.register::<comp::Vel>();
ecs.register::<comp::Ori>();
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::Controller>();
ecs.register::<comp::Attacking>();
ecs.register::<comp::Wielding>();
ecs.register::<comp::Rolling>();
ecs.register::<comp::Gliding>();
ecs.register::<comp::ActionState>();
// Register client-local components
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Jumping>();
// Register server-local components
ecs.register::<comp::Last<comp::Pos>>();
ecs.register::<comp::Last<comp::Vel>>();
ecs.register::<comp::Last<comp::Ori>>();
ecs.register::<comp::Last<comp::ActionState>>();
ecs.register::<comp::Agent>();
ecs.register::<comp::Respawning>();
ecs.register::<comp::Dying>();
ecs.register::<comp::ForceUpdate>();
ecs.register::<comp::Inventory>();
// Controller effects
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::Attacking>();
ecs.register::<comp::Wielding>();
ecs.register::<comp::Rolling>();
ecs.register::<comp::Gliding>();
// Register synced resources used by the ECS.
ecs.add_resource_synced(TimeOfDay(0.0));

View File

@ -46,7 +46,7 @@ impl<'a> System<'a> for Sys {
Vec2::from(pos.0 - tgt_pos).normalized()
} else {
Vec2::zero()
};
} * -10.0;
}
_ => controller.move_dir = Vec2::zero(),
}

View File

@ -155,56 +155,43 @@ lazy_static! {
}
fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let (opt_x, opt_y, opt_z) = scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32);
match (opt_x, opt_y, opt_z) {
(Some(x), Some(y), Some(z)) => {
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(current_pos) => {
server
.state
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::private(String::from("You have no position!")),
),
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(current_pos) => {
server
.state
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::private(String::from("You have no position!")),
),
}
_ => server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string))),
}
}
fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let (mut opt_x, mut opt_y, mut opt_z) = (None, None, None);
if let Ok((opt_x1, opt_y1, opt_z1)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
opt_x = Some(opt_x1);
opt_y = Some(opt_y1);
opt_z = Some(opt_z1);
}
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(_pos) => match (opt_x, opt_y, opt_z) {
(Some(x), Some(y), Some(z)) => {
server
.state
.write_component(entity, comp::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
}
_ => server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string))),
},
None => {
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
if server
.state
.read_component_cloned::<comp::Pos>(entity)
.is_some()
{
server
.state
.write_component(entity, comp::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
} else {
server.clients.notify(
entity,
ServerMsg::private(String::from("You don't have any position!")),
ServerMsg::private(String::from("You don't have a position!")),
);
}
} else {
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string)));
}
}
@ -218,13 +205,13 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &
}
fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let time = scan_fmt!(&args, action.arg_fmt, String);
let time = scan_fmt_some!(&args, action.arg_fmt, String);
let new_time = match time.as_ref().map(|s| s.as_str()) {
Ok("night") => NaiveTime::from_hms(0, 0, 0),
Ok("dawn") => NaiveTime::from_hms(5, 0, 0),
Ok("day") => NaiveTime::from_hms(12, 0, 0),
Ok("dusk") => NaiveTime::from_hms(17, 0, 0),
Ok(n) => match n.parse() {
Some("night") => NaiveTime::from_hms(0, 0, 0),
Some("dawn") => NaiveTime::from_hms(5, 0, 0),
Some("day") => NaiveTime::from_hms(12, 0, 0),
Some("dusk") => NaiveTime::from_hms(17, 0, 0),
Some(n) => match n.parse() {
Ok(n) => n,
Err(_) => match NaiveTime::parse_from_str(n, "%H:%M") {
Ok(time) => time,
@ -237,7 +224,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
}
},
},
Err(_) => {
None => {
let time_in_seconds = server.state.ecs_mut().read_resource::<TimeOfDay>().0;
let current_time = NaiveTime::from_num_seconds_from_midnight(time_in_seconds as u32, 0);
server.clients.notify(
@ -264,100 +251,90 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
}
fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_hp = scan_fmt!(&args, action.arg_fmt, u32);
match server
.state
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(entity)
{
Some(stats) => match opt_hp {
Ok(hp) => stats.health.set_to(hp, comp::HealthSource::Command),
Err(_) => {
server.clients.notify(
entity,
ServerMsg::private(String::from("You must specify health amount!")),
);
}
},
None => server.clients.notify(
if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) {
if let Some(stats) = server
.state
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(entity)
{
stats.health.set_to(hp, comp::HealthSource::Command);
} else {
server.clients.notify(
entity,
ServerMsg::private(String::from("You have no position.")),
);
}
} else {
server.clients.notify(
entity,
ServerMsg::private(String::from("You have no position.")),
),
ServerMsg::private(String::from("You must specify health amount!")),
);
}
}
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias {
Ok(alias) => {
server
.state
.ecs_mut()
.write_storage::<comp::Player>()
.get_mut(entity)
.map(|player| player.alias = alias);
}
Err(_) => server
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
server
.state
.ecs_mut()
.write_storage::<comp::Player>()
.get_mut(entity)
.map(|player| player.alias = alias);
} else {
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string))),
.notify(entity, ServerMsg::private(String::from(action.help_string)));
}
}
fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias {
Ok(alias) => {
let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity);
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(_pos) => match opt_player {
Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) {
Some(pos) => {
server.state.write_component(entity, pos);
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::private(format!(
"Unable to teleport to player '{}'!",
alias
)),
),
},
None => {
server.clients.notify(
entity,
ServerMsg::private(format!("Player '{}' not found!", alias)),
);
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string)));
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity);
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(_pos) => match opt_player {
Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) {
Some(pos) => {
server.state.write_component(entity, pos);
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)),
),
},
None => {
server.clients.notify(
entity,
ServerMsg::private(format!("Player '{}' not found!", alias)),
);
server
.clients
.notify(entity, ServerMsg::private(format!("You have no position!")));
.notify(entity, ServerMsg::private(String::from(action.help_string)));
}
},
None => {
server
.clients
.notify(entity, ServerMsg::private(format!("You have no position!")));
}
}
Err(_) => server
} else {
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string))),
.notify(entity, ServerMsg::private(String::from(action.help_string)));
}
}
fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
match scan_fmt!(&args, action.arg_fmt, String, NpcKind, String) {
Ok((opt_align, id, opt_amount)) => {
match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) {
(Some(opt_align), Some(id), opt_amount) => {
if let Some(agent) = alignment_to_agent(&opt_align, entity) {
let _objtype = scan_fmt!(&args, action.arg_fmt, String);
let amount = Some(opt_amount)
let amount = opt_amount
.map_or(Some(1), |a| a.parse().ok())
.and_then(|a| if a > 0 { Some(a) } else { None })
.unwrap();
@ -390,7 +367,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
}
}
}
Err(_) => {
_ => {
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string)));
@ -638,7 +615,7 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C
}
fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_s = scan_fmt!(&args, action.arg_fmt, f32);
let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32);
if server
.state
@ -646,7 +623,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.get(entity)
.is_some()
{
if let Ok(s) = opt_s {
if let Some(s) = opt_s {
if let Some(light) = server
.state
.ecs()
@ -680,7 +657,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
comp::LightEmitter {
offset: Vec3::new(0.5, 0.2, 0.8),
col: Rgb::new(1.0, 0.75, 0.3),
strength: if let Ok(s) = opt_s { s.max(0.0) } else { 6.0 },
strength: if let Some(s) = opt_s { s.max(0.0) } else { 6.0 },
},
);
@ -692,64 +669,56 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
}
fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias {
Ok(alias) => {
let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity);
let msg = &args[alias.len()..args.len()];
match opt_player {
Some(player) => {
if player != entity {
if msg.len() > 1 {
let opt_name = ecs
.read_storage::<comp::Player>()
.get(entity)
.map(|s| s.alias.clone());
match opt_name {
Some(name) => {
server.clients.notify(
player,
ServerMsg::tell(format!("[{}] tells you:{}", name, msg)),
);
server.clients.notify(
entity,
ServerMsg::tell(format!("You tell [{}]:{}", alias, msg)),
);
}
None => {
server.clients.notify(
entity,
ServerMsg::private(String::from("Failed to send message.")),
);
}
}
} else {
server.clients.notify(
entity,
ServerMsg::private(format!("[{}] wants to talk to you.", alias)),
);
}
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs();
let msg = &args[alias.len()..args.len()];
if let Some(player) = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity)
{
if player != entity {
if msg.len() > 1 {
if let Some(name) = ecs
.read_storage::<comp::Player>()
.get(entity)
.map(|s| s.alias.clone())
{
server.clients.notify(
player,
ServerMsg::tell(format!("[{}] tells you:{}", name, msg)),
);
server.clients.notify(
entity,
ServerMsg::tell(format!("You tell [{}]:{}", alias, msg)),
);
} else {
server.clients.notify(
entity,
ServerMsg::private(format!("You can't /tell yourself.")),
ServerMsg::private(String::from("Failed to send message.")),
);
}
}
None => {
} else {
server.clients.notify(
entity,
ServerMsg::private(format!("Player '{}' not found!", alias)),
ServerMsg::private(format!("[{}] wants to talk to you.", alias)),
);
}
} else {
server.clients.notify(
entity,
ServerMsg::private(format!("You can't /tell yourself.")),
);
}
} else {
server.clients.notify(
entity,
ServerMsg::private(format!("Player '{}' not found!", alias)),
);
}
Err(_) => server
} else {
server
.clients
.notify(entity, ServerMsg::private(String::from(action.help_string))),
.notify(entity, ServerMsg::private(String::from(action.help_string)));
}
}

View File

@ -71,13 +71,11 @@ pub struct Server {
impl Server {
/// Create a new `Server` bound to the default socket.
#[allow(dead_code)]
pub fn new(settings: ServerSettings) -> Result<Self, Error> {
Self::bind(settings.address, settings)
}
/// Create a new server bound to the given socket.
#[allow(dead_code)]
pub fn bind<A: Into<SocketAddr>>(addrs: A, settings: ServerSettings) -> Result<Self, Error> {
let (chunk_tx, chunk_rx) = mpsc::channel();
@ -114,31 +112,26 @@ impl Server {
Ok(this)
}
#[allow(dead_code)]
pub fn with_thread_pool(mut self, thread_pool: ThreadPool) -> Self {
self.thread_pool = thread_pool;
self
}
/// Get a reference to the server's game state.
#[allow(dead_code)]
pub fn state(&self) -> &State {
&self.state
}
/// Get a mutable reference to the server's game state.
#[allow(dead_code)]
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
/// Get a reference to the server's world.
#[allow(dead_code)]
pub fn world(&self) -> &World {
&self.world
}
/// Build a non-player character.
#[allow(dead_code)]
pub fn create_npc(
&mut self,
pos: comp::Pos,
@ -159,7 +152,6 @@ impl Server {
}
/// Build a static object entity
#[allow(dead_code)]
pub fn create_object(
&mut self,
pos: comp::Pos,
@ -207,7 +199,6 @@ impl Server {
}
/// 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<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most server-side things are
// managed from here, and as such it's important that it stays organised. Please consult
@ -381,7 +372,6 @@ impl Server {
}
/// Clean up the server after a tick.
#[allow(dead_code)]
pub fn cleanup(&mut self) {
// Cleanup the local state
self.state.cleanup();
@ -687,23 +677,38 @@ impl Server {
// Save player metadata (for example the username).
state.write_component(entity, player);
// Sync physics
// Sync physics of all entities
for (&uid, &pos, vel, ori, action_state) in (
&state.ecs().read_storage::<Uid>(),
&state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Pos>(), // We assume all these entities have a position
state.ecs().read_storage::<comp::Vel>().maybe(),
state.ecs().read_storage::<comp::Ori>().maybe(),
state.ecs().read_storage::<comp::ActionState>().maybe(),
)
.join()
{
client.notify(ServerMsg::EntityPhysics {
client.notify(ServerMsg::EntityPos {
entity: uid.into(),
pos,
vel: vel.copied(),
ori: ori.copied(),
action_state: action_state.copied(),
});
if let Some(vel) = vel.copied() {
client.notify(ServerMsg::EntityVel {
entity: uid.into(),
vel,
});
}
if let Some(ori) = ori.copied() {
client.notify(ServerMsg::EntityOri {
entity: uid.into(),
ori,
});
}
if let Some(action_state) = action_state.copied() {
client.notify(ServerMsg::EntityActionState {
entity: uid.into(),
action_state,
});
}
}
// Tell the client its request was successful.
@ -716,8 +721,9 @@ impl Server {
self.clients
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
// TODO: Move this into some new method like `handle_sys_outputs` right after ticking the world
// Handle deaths.
let ecs = &self.state.ecs();
let ecs = self.state.ecs_mut();
let clients = &mut self.clients;
let todo_kill = (&ecs.entities(), &ecs.read_storage::<comp::Dying>())
.join()
@ -774,20 +780,17 @@ impl Server {
// Actually kill them
for entity in todo_kill {
if let Some(client) = self.clients.get_mut(&entity) {
self.state.write_component(entity, comp::Vel(Vec3::zero()));
self.state.write_component(entity, comp::ForceUpdate);
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
client.force_state(ClientState::Dead);
} else {
let _ = self.state.ecs_mut().delete_entity_synced(entity);
let _ = ecs.delete_entity_synced(entity);
continue;
}
}
// Handle respawns
let todo_respawn = (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Respawning>(),
)
let todo_respawn = (&ecs.entities(), &ecs.read_storage::<comp::Respawning>())
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
@ -795,69 +798,137 @@ impl Server {
for entity in todo_respawn {
if let Some(client) = self.clients.get_mut(&entity) {
client.allow_state(ClientState::Character);
self.state
.ecs_mut()
.write_storage::<comp::Stats>()
ecs.write_storage::<comp::Stats>()
.get_mut(entity)
.map(|stats| stats.revive());
self.state
.ecs_mut()
.write_storage::<comp::Pos>()
ecs.write_storage::<comp::Pos>()
.get_mut(entity)
.map(|pos| pos.0.z += 20.0);
self.state.write_component(entity, comp::ForceUpdate);
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
}
}
// Sync physics
for (entity, &uid, &pos, vel, ori, action_state, force_update) in (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<Uid>(),
&self.state.ecs().read_storage::<comp::Pos>(),
self.state.ecs().read_storage::<comp::Vel>().maybe(),
self.state.ecs().read_storage::<comp::Ori>().maybe(),
self.state.ecs().read_storage::<comp::ActionState>().maybe(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
for (entity, &uid, &pos, force_update) in (
&ecs.entities(),
&ecs.read_storage::<Uid>(),
&ecs.read_storage::<comp::Pos>(),
ecs.read_storage::<comp::ForceUpdate>().maybe(),
)
.join()
{
let msg = ServerMsg::EntityPhysics {
entity: uid.into(),
pos,
vel: vel.copied(),
ori: ori.copied(),
action_state: action_state.copied(),
};
let state = &self.state;
let clients = &mut self.clients;
let in_vd = |entity| {
// Get client position.
let client_pos = match state.ecs().read_storage::<comp::Pos>().get(entity) {
Some(pos) => pos.0,
None => return false,
};
// Get client view distance
let client_vd = match state.ecs().read_storage::<comp::Player>().get(entity) {
Some(comp::Player {
view_distance: Some(vd),
..
}) => *vd,
_ => return false,
};
(pos.0 - client_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| {
(d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
})
.magnitude_squared()
< client_vd.pow(2)
if let (Some(client_pos), Some(client_vd)) = (
ecs.read_storage::<comp::Pos>().get(entity),
ecs.read_storage::<comp::Player>()
.get(entity)
.map(|pl| pl.view_distance)
.and_then(|v| v),
) {
{
// Check if the entity is in the client's range
(pos.0 - client_pos.0)
.map2(TerrainChunkSize::SIZE, |d, sz| {
(d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
})
.magnitude_squared()
< client_vd.pow(2)
}
} else {
false
}
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
let mut last_pos = ecs.write_storage::<comp::Last<comp::Pos>>();
let mut last_vel = ecs.write_storage::<comp::Last<comp::Vel>>();
let mut last_ori = ecs.write_storage::<comp::Last<comp::Ori>>();
let mut last_action_state = ecs.write_storage::<comp::Last<comp::ActionState>>();
if let (
Some(client_pos),
Some(client_vel),
Some(client_ori),
Some(client_action_state),
) = (
ecs.read_storage::<comp::Pos>().get(entity),
ecs.read_storage::<comp::Vel>().get(entity),
ecs.read_storage::<comp::Ori>().get(entity),
ecs.read_storage::<comp::ActionState>().get(entity),
) {
// If nothing changed...
if last_pos
.get(entity)
.map(|&l| l != *client_pos)
.unwrap_or(true)
{
let _ = last_pos.insert(entity, comp::Last(*client_pos));
let msg = ServerMsg::EntityPos {
entity: uid.into(),
pos: *client_pos,
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
}
}
if last_vel
.get(entity)
.map(|&l| l != *client_vel)
.unwrap_or(true)
{
let _ = last_vel.insert(entity, comp::Last(*client_vel));
let msg = ServerMsg::EntityVel {
entity: uid.into(),
vel: *client_vel,
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
}
}
if last_ori
.get(entity)
.map(|&l| l != *client_ori)
.unwrap_or(true)
{
let _ = last_ori.insert(entity, comp::Last(*client_ori));
let msg = ServerMsg::EntityOri {
entity: uid.into(),
ori: *client_ori,
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
}
}
if last_action_state
.get(entity)
.map(|&l| l != *client_action_state)
.unwrap_or(true)
{
let _ = last_action_state.insert(entity, comp::Last(*client_action_state));
let msg = ServerMsg::EntityActionState {
entity: uid.into(),
action_state: *client_action_state,
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
}
}
}
}