Fixed physics sync

Former-commit-id: 86d3af46c69f6566f4cab8d93ecf27636a9563cf
This commit is contained in:
Joshua Barretto 2019-04-14 21:30:27 +01:00
parent 1e90cf68d5
commit c98d841890
7 changed files with 108 additions and 49 deletions

View File

@ -40,7 +40,7 @@ pub struct Client {
tick: u64,
state: State,
player: Option<EcsEntity>,
player: EcsEntity,
view_distance: u64,
pending_chunks: HashSet<Vec3<i32>>,
@ -68,7 +68,7 @@ impl Client {
let (state, player) = match postbox.next_message() {
Some(ServerMsg::Handshake { ecs_state, player_entity }) => {
let mut state = State::from_state_package(ecs_state);
let player_entity = state.ecs().entity_from_uid(player_entity);
let player_entity = state.ecs().entity_from_uid(player_entity).ok_or(Error::ServerWentMad)?;
(state, player_entity)
},
_ => return Err(Error::ServerWentMad),
@ -107,7 +107,7 @@ impl Client {
/// Get the player entity
#[allow(dead_code)]
pub fn player(&self) -> Option<EcsEntity> {
pub fn player(&self) -> EcsEntity {
self.player
}
@ -149,13 +149,17 @@ impl Client {
});
// Step 1
if let Some(ecs_entity) = self.player {
if
self.state.read_storage::<comp::phys::Pos>().get(self.player).is_some() &&
self.state.read_storage::<comp::phys::Vel>().get(self.player).is_some() &&
self.state.read_storage::<comp::phys::Dir>().get(self.player).is_some() == true
{
// TODO: remove this
const PLAYER_VELOCITY: f32 = 100.0;
// TODO: Set acceleration instead
self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1));
self.state.write_component(self.player, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1));
if input.move_dir.magnitude() > 0.01 {
self.state.write_component(ecs_entity, comp::phys::Dir(input.move_dir.normalized().into()));
self.state.write_component(self.player, comp::phys::Dir(input.move_dir.normalized().into()));
}
}
@ -163,32 +167,28 @@ impl Client {
self.state.tick(dt);
// Update the server about the player's physics attributes
if let Some(ecs_entity) = self.player {
match (
self.state.read_storage().get(ecs_entity).cloned(),
self.state.read_storage().get(ecs_entity).cloned(),
self.state.read_storage().get(ecs_entity).cloned(),
) {
(Some(pos), Some(vel), Some(dir)) => {
self.postbox.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
},
_ => {},
}
match (
self.state.read_storage().get(self.player).cloned(),
self.state.read_storage().get(self.player).cloned(),
self.state.read_storage().get(self.player).cloned(),
) {
(Some(pos), Some(vel), Some(dir)) => {
self.postbox.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
},
_ => {},
}
// Request chunks from the server
if let Some(player_entity) = self.player {
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(player_entity) {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(self.player) {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
for i in chunk_pos.x - 0..chunk_pos.x + 1 {
for j in chunk_pos.y - 0..chunk_pos.y + 1 {
for k in 0..3 {
let key = chunk_pos + Vec3::new(i, j, k);
if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains(&key) {
self.postbox.send_message(ClientMsg::TerrainChunkRequest { key });
self.pending_chunks.insert(key);
}
for i in chunk_pos.x - 1..chunk_pos.x + 1 {
for j in chunk_pos.y - 1..chunk_pos.y + 1 {
for k in -1..3 {
let key = chunk_pos + Vec3::new(i, j, k);
if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains(&key) {
self.postbox.send_message(ClientMsg::TerrainChunkRequest { key });
self.pending_chunks.insert(key);
}
}
}
@ -224,8 +224,16 @@ impl Client {
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
ServerMsg::Pong => {},
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here!
ServerMsg::SetPlayerEntity(uid) => self.player = self.state.ecs().entity_from_uid(uid).unwrap(), // TODO: Don't unwrap here!
ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package),
ServerMsg::EntityPhysics { entity, pos, vel, dir } => match self.state.ecs().entity_from_uid(entity) {
Some(entity) => {
self.state.write_component(entity, pos);
self.state.write_component(entity, vel);
self.state.write_component(entity, dir);
},
None => {},
},
ServerMsg::TerrainChunkUpdate { key, chunk } => {
self.state.insert_chunk(key, *chunk);
self.pending_chunks.remove(&key);

View File

@ -1,4 +1,4 @@
use specs::{Component, VecStorage, FlaggedStorage};
use specs::{Component, VecStorage, FlaggedStorage, NullStorage};
use vek::*;
// Pos
@ -27,3 +27,12 @@ pub struct Dir(pub Vec3<f32>);
impl Component for Dir {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
// Dir
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct ForceUpdate;
impl Component for ForceUpdate {
type Storage = NullStorage<Self>;
}

View File

@ -1,5 +1,8 @@
use vek::*;
use crate::terrain::TerrainChunk;
use crate::{
comp,
terrain::TerrainChunk,
};
use super::EcsPacket;
#[derive(Clone, Serialize, Deserialize)]
@ -14,6 +17,12 @@ pub enum ServerMsg {
Chat(String),
SetPlayerEntity(u64),
EcsSync(sphynx::SyncPackage<EcsPacket>),
EntityPhysics {
entity: u64,
pos: comp::phys::Pos,
vel: comp::phys::Vel,
dir: comp::phys::Dir,
},
TerrainChunkUpdate {
key: Vec3<i32>,
chunk: Box<TerrainChunk>,

View File

@ -1,3 +1,6 @@
// Reexports
pub use sphynx::Uid;
use std::{
time::Duration,
collections::HashSet,
@ -90,12 +93,14 @@ impl State {
// Create a new Sphynx ECS world
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsPacket>) {
// Register synced components
ecs.register_synced::<comp::phys::Pos>();
ecs.register_synced::<comp::phys::Vel>();
ecs.register_synced::<comp::phys::Dir>();
ecs.register_synced::<comp::Character>();
ecs.register_synced::<comp::Player>();
// Register unsynched (or synced by other means) components
ecs.internal_mut().register::<comp::phys::Pos>();
ecs.internal_mut().register::<comp::phys::Vel>();
ecs.internal_mut().register::<comp::phys::Dir>();
// Register resources used by the ECS
ecs.internal_mut().add_resource(TimeOfDay(0.0));
ecs.internal_mut().add_resource(Time(0.0));

View File

@ -27,7 +27,7 @@ use vek::*;
use threadpool::ThreadPool;
use common::{
comp,
state::State,
state::{State, Uid},
net::PostOffice,
msg::{ServerMsg, ClientMsg},
terrain::TerrainChunk,
@ -73,8 +73,11 @@ impl Server {
pub fn new() -> Result<Self, Error> {
let (chunk_tx, chunk_rx) = mpsc::channel();
let mut state = State::new();
state.ecs_mut().internal_mut().register::<comp::phys::ForceUpdate>();
Ok(Self {
state: State::new(),
state,
world: World::new(),
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
@ -184,6 +187,9 @@ impl Server {
.create_entity_synced()
.build();
// Make sure the entity gets properly created
self.state.ecs_mut().internal_mut().maintain();
self.clients.add(entity, Client {
state: ClientState::Connecting,
postbox,
@ -229,6 +235,7 @@ impl Server {
if let Some(character) = character {
state.write_component(entity, character);
}
state.write_component(entity, comp::phys::ForceUpdate);
client.state = ClientState::Connected;
@ -303,7 +310,7 @@ impl Server {
// Handle client disconnects
for entity in disconnected_clients {
state.ecs_mut().delete_entity_synced(entity);
state.ecs_mut().delete_entity_synced(entity).unwrap();
frontend_events.push(Event::ClientDisconnected {
entity,
@ -320,7 +327,33 @@ impl Server {
/// Sync client states with the most up to date information
fn sync_clients(&mut self) {
// Sync 'logical' state using Sphynx
self.clients.notify_connected(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
// Sync 'physical' state
for (entity, &uid, &pos, &vel, &dir, force_update) in (
&self.state.ecs().internal().entities(),
&self.state.ecs().internal().read_storage::<Uid>(),
&self.state.ecs().internal().read_storage::<comp::phys::Pos>(),
&self.state.ecs().internal().read_storage::<comp::phys::Vel>(),
&self.state.ecs().internal().read_storage::<comp::phys::Dir>(),
self.state.ecs().internal().read_storage::<comp::phys::ForceUpdate>().maybe(),
).join() {
let msg = ServerMsg::EntityPhysics {
entity: uid.into(),
pos,
vel,
dir,
};
match force_update {
Some(_) => self.clients.notify_connected(msg),
None => self.clients.notify_connected_except(entity, msg),
}
}
// Remove all force flags
self.state.ecs_mut().internal_mut().write_storage::<comp::phys::ForceUpdate>().clear();
}
pub fn generate_chunk(&mut self, key: Vec3<i32>) {

View File

@ -109,15 +109,12 @@ impl Scene {
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
// Get player position
let player_pos = client
.player()
.and_then(|ent| client
.state()
.ecs()
.internal()
.read_storage::<comp::phys::Pos>()
.get(ent)
.map(|pos| pos.0)
)
.state()
.ecs()
.internal()
.read_storage::<comp::phys::Pos>()
.get(client.player())
.map(|pos| pos.0)
.unwrap_or(Vec3::zero());
// Alter camera position to match player

View File

@ -143,9 +143,7 @@ impl Window {
pub fn grab_cursor(&mut self, grab: bool) {
self.cursor_grabbed = grab;
self.window.hide_cursor(grab);
self.window
.grab_cursor(grab)
.expect("Failed to grab/ungrab cursor");
let _ = self.window.grab_cursor(grab);
}
pub fn needs_refresh_resize(&mut self) {