mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Actually send deletion messages
This commit is contained in:
parent
71cce03f29
commit
e49cafafbf
@ -17,7 +17,7 @@ opt-level = 2
|
|||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = false
|
# debug = false
|
||||||
codegen-units = 8
|
codegen-units = 8
|
||||||
lto = false
|
lto = false
|
||||||
incremental = true
|
incremental = true
|
||||||
|
@ -562,10 +562,15 @@ impl Client {
|
|||||||
self.state.ecs_mut().apply_entity_package(entity_package)
|
self.state.ecs_mut().apply_entity_package(entity_package)
|
||||||
}
|
}
|
||||||
ServerMsg::DeleteEntity(entity) => {
|
ServerMsg::DeleteEntity(entity) => {
|
||||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
if self
|
||||||
if entity != self.entity {
|
.state
|
||||||
let _ = self.state.ecs_mut().delete_entity(entity);
|
.read_component_cloned::<Uid>(self.entity)
|
||||||
}
|
.map(|u| u.into())
|
||||||
|
!= Some(entity)
|
||||||
|
{
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.delete_entity_and_clear_from_uid_allocator(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerMsg::EntityPos { entity, pos } => {
|
ServerMsg::EntityPos { entity, pos } => {
|
||||||
|
@ -25,9 +25,6 @@ impl sync::ResPacket for EcsResPacket {
|
|||||||
sum_type! {
|
sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPacket {
|
pub enum EcsCompPacket {
|
||||||
Pos(comp::Pos),
|
|
||||||
Vel(comp::Vel),
|
|
||||||
Ori(comp::Ori),
|
|
||||||
Body(comp::Body),
|
Body(comp::Body),
|
||||||
Player(comp::Player),
|
Player(comp::Player),
|
||||||
CanBuild(comp::CanBuild),
|
CanBuild(comp::CanBuild),
|
||||||
@ -48,9 +45,6 @@ sum_type! {
|
|||||||
sum_type! {
|
sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPhantom {
|
pub enum EcsCompPhantom {
|
||||||
Pos(PhantomData<comp::Pos>),
|
|
||||||
Vel(PhantomData<comp::Vel>),
|
|
||||||
Ori(PhantomData<comp::Ori>),
|
|
||||||
Body(PhantomData<comp::Body>),
|
Body(PhantomData<comp::Body>),
|
||||||
Player(PhantomData<comp::Player>),
|
Player(PhantomData<comp::Player>),
|
||||||
CanBuild(PhantomData<comp::CanBuild>),
|
CanBuild(PhantomData<comp::CanBuild>),
|
||||||
@ -70,9 +64,6 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
type Phantom = EcsCompPhantom;
|
type Phantom = EcsCompPhantom;
|
||||||
fn apply_insert(self, entity: specs::Entity, world: &specs::World) {
|
fn apply_insert(self, entity: specs::Entity, world: &specs::World) {
|
||||||
match self {
|
match self {
|
||||||
EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world),
|
|
||||||
EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world),
|
|
||||||
EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world),
|
|
||||||
EcsCompPacket::Body(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Body(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Player(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Player(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world),
|
||||||
@ -90,9 +81,6 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
}
|
}
|
||||||
fn apply_modify(self, entity: specs::Entity, world: &specs::World) {
|
fn apply_modify(self, entity: specs::Entity, world: &specs::World) {
|
||||||
match self {
|
match self {
|
||||||
EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world),
|
|
||||||
EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world),
|
|
||||||
EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world),
|
|
||||||
EcsCompPacket::Body(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Body(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Player(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Player(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::CanBuild(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::CanBuild(comp) => sync::handle_modify(comp, entity, world),
|
||||||
@ -110,9 +98,6 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
}
|
}
|
||||||
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
|
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
|
||||||
match phantom {
|
match phantom {
|
||||||
EcsCompPhantom::Pos(_) => sync::handle_remove::<comp::Pos>(entity, world),
|
|
||||||
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
|
|
||||||
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
|
||||||
EcsCompPhantom::Body(_) => sync::handle_remove::<comp::Body>(entity, world),
|
EcsCompPhantom::Body(_) => sync::handle_remove::<comp::Body>(entity, world),
|
||||||
EcsCompPhantom::Player(_) => sync::handle_remove::<comp::Player>(entity, world),
|
EcsCompPhantom::Player(_) => sync::handle_remove::<comp::Player>(entity, world),
|
||||||
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
|
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
|
||||||
|
@ -104,6 +104,16 @@ impl RegionMap {
|
|||||||
// TODO special case large entities
|
// TODO special case large entities
|
||||||
pub fn tick(&mut self, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>, entities: Entities) {
|
pub fn tick(&mut self, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>, entities: Entities) {
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
|
// Clear events within each region
|
||||||
|
for i in 0..self.regions.len() {
|
||||||
|
self.regions
|
||||||
|
.get_index_mut(i)
|
||||||
|
.map(|(_, v)| v)
|
||||||
|
.unwrap()
|
||||||
|
.events
|
||||||
|
.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Add any untracked entites
|
// Add any untracked entites
|
||||||
for (pos, id) in (&pos, &entities, !&self.tracked_entities)
|
for (pos, id) in (&pos, &entities, !&self.tracked_entities)
|
||||||
.join()
|
.join()
|
||||||
@ -118,14 +128,6 @@ impl RegionMap {
|
|||||||
let mut regions_to_remove = Vec::new();
|
let mut regions_to_remove = Vec::new();
|
||||||
|
|
||||||
for i in 0..self.regions.len() {
|
for i in 0..self.regions.len() {
|
||||||
// Clear events within each region
|
|
||||||
self.regions
|
|
||||||
.get_index_mut(i)
|
|
||||||
.map(|(_, v)| v)
|
|
||||||
.unwrap()
|
|
||||||
.events
|
|
||||||
.clear();
|
|
||||||
|
|
||||||
for (maybe_pos, _maybe_vel, id) in (
|
for (maybe_pos, _maybe_vel, id) in (
|
||||||
pos.maybe(),
|
pos.maybe(),
|
||||||
vel.maybe(),
|
vel.maybe(),
|
||||||
@ -215,6 +217,47 @@ impl RegionMap {
|
|||||||
pub fn key_pos(key: Vec2<i32>) -> Vec2<i32> {
|
pub fn key_pos(key: Vec2<i32>) -> Vec2<i32> {
|
||||||
key.map(|e| e << REGION_LOG2)
|
key.map(|e| e << REGION_LOG2)
|
||||||
}
|
}
|
||||||
|
/// Finds the region where a given entity is located using a given position to speed up the search
|
||||||
|
pub fn find_region(&self, entity: specs::Entity, pos: Vec3<f32>) -> Option<Vec2<i32>> {
|
||||||
|
let id = entity.id();
|
||||||
|
// Compute key for most likely region
|
||||||
|
let key = Self::pos_key(pos.map(|e| e as i32));
|
||||||
|
// Get region
|
||||||
|
if let Some(region) = self.regions.get(&key) {
|
||||||
|
if region.entities().contains(id) {
|
||||||
|
return Some(key);
|
||||||
|
} else {
|
||||||
|
// Check neighbors
|
||||||
|
for i in 0..8 {
|
||||||
|
if let Some(idx) = region.neighbors[i] {
|
||||||
|
let (key, region) = self.regions.get_index(idx).unwrap();
|
||||||
|
if region.entities().contains(id) {
|
||||||
|
return Some(*key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check neighbors
|
||||||
|
for i in 0..8 {
|
||||||
|
let key = key + NEIGHBOR_OFFSETS[i];
|
||||||
|
if let Some(region) = self.regions.get(&key) {
|
||||||
|
if region.entities().contains(id) {
|
||||||
|
return Some(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan though all regions
|
||||||
|
for (key, region) in self.iter() {
|
||||||
|
if region.entities().contains(id) {
|
||||||
|
return Some(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
fn key_index(&self, key: Vec2<i32>) -> Option<usize> {
|
fn key_index(&self, key: Vec2<i32>) -> Option<usize> {
|
||||||
self.regions.get_full(&key).map(|(i, _, _)| i)
|
self.regions.get_full(&key).map(|(i, _, _)| i)
|
||||||
}
|
}
|
||||||
|
@ -148,10 +148,10 @@ impl State {
|
|||||||
ecs.register::<comp::Waypoint>();
|
ecs.register::<comp::Waypoint>();
|
||||||
|
|
||||||
// Register synced resources used by the ECS.
|
// Register synced resources used by the ECS.
|
||||||
ecs.add_resource(Time(0.0));
|
|
||||||
ecs.add_resource(TimeOfDay(0.0));
|
ecs.add_resource(TimeOfDay(0.0));
|
||||||
|
|
||||||
// Register unsynced resources used by the ECS.
|
// Register unsynced resources used by the ECS.
|
||||||
|
ecs.add_resource(Time(0.0));
|
||||||
ecs.add_resource(DeltaTime(0.0));
|
ecs.add_resource(DeltaTime(0.0));
|
||||||
ecs.add_resource(TerrainGrid::new().unwrap());
|
ecs.add_resource(TerrainGrid::new().unwrap());
|
||||||
ecs.add_resource(BlockChange::default());
|
ecs.add_resource(BlockChange::default());
|
||||||
|
@ -2,6 +2,7 @@ use super::{
|
|||||||
track::{Tracker, UpdateTracker},
|
track::{Tracker, UpdateTracker},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
|
use log::error;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use specs::{shred::Resource, Component, Entity, Join, ReadStorage, World};
|
use specs::{shred::Resource, Component, Entity, Join, ReadStorage, World};
|
||||||
use std::{
|
use std::{
|
||||||
@ -24,7 +25,9 @@ pub trait ResPacket: Clone + Debug + Send + 'static {
|
|||||||
|
|
||||||
/// Useful for implementing CompPacket trait
|
/// Useful for implementing CompPacket trait
|
||||||
pub fn handle_insert<C: Component>(comp: C, entity: Entity, world: &World) {
|
pub fn handle_insert<C: Component>(comp: C, entity: Entity, world: &World) {
|
||||||
let _ = world.write_storage::<C>().insert(entity, comp);
|
if let Err(err) = world.write_storage::<C>().insert(entity, comp) {
|
||||||
|
error!("Error inserting component: {:?}", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
/// Useful for implementing CompPacket trait
|
/// Useful for implementing CompPacket trait
|
||||||
pub fn handle_modify<C: Component>(comp: C, entity: Entity, world: &World) {
|
pub fn handle_modify<C: Component>(comp: C, entity: Entity, world: &World) {
|
||||||
@ -102,6 +105,7 @@ impl<P: CompPacket> SyncPackage<P> {
|
|||||||
uids: &ReadStorage<'a, Uid>,
|
uids: &ReadStorage<'a, Uid>,
|
||||||
uid_tracker: &UpdateTracker<Uid>,
|
uid_tracker: &UpdateTracker<Uid>,
|
||||||
filter: impl Join + Copy,
|
filter: impl Join + Copy,
|
||||||
|
deleted_entities: Vec<u64>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Add created and deleted entities
|
// Add created and deleted entities
|
||||||
let created_entities = (uids, filter, uid_tracker.inserted())
|
let created_entities = (uids, filter, uid_tracker.inserted())
|
||||||
@ -110,10 +114,15 @@ impl<P: CompPacket> SyncPackage<P> {
|
|||||||
.collect();
|
.collect();
|
||||||
// TODO: handle modified uid?
|
// TODO: handle modified uid?
|
||||||
//created_entities.append(&mut (uids, filter, uid_tracker.inserted()).join().map(|(uid, _, _)| uid).collect());
|
//created_entities.append(&mut (uids, filter, uid_tracker.inserted()).join().map(|(uid, _, _)| uid).collect());
|
||||||
let deleted_entities = (uids, filter, uid_tracker.removed())
|
// let deleted_entities = (uids.maybe(), filter, uid_tracker.removed())
|
||||||
.join()
|
// .join()
|
||||||
.map(|(uid, _, _)| (*uid).into())
|
// Why doesn't this panic??
|
||||||
.collect();
|
// .map(|(uid, _, _)| Into::<u64>::into(*uid.unwrap()))
|
||||||
|
// .collect::<Vec<_>>();
|
||||||
|
//let len = deleted_entities.len();
|
||||||
|
//if len > 0 {
|
||||||
|
// println!("deleted {} in sync message", len);
|
||||||
|
// }
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
comp_updates: Vec::new(),
|
comp_updates: Vec::new(),
|
||||||
@ -124,7 +133,6 @@ impl<P: CompPacket> SyncPackage<P> {
|
|||||||
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
|
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
|
||||||
mut self,
|
mut self,
|
||||||
uids: &ReadStorage<'a, Uid>,
|
uids: &ReadStorage<'a, Uid>,
|
||||||
uid_tracker: &UpdateTracker<Uid>,
|
|
||||||
tracker: &impl Tracker<C, P>,
|
tracker: &impl Tracker<C, P>,
|
||||||
storage: &ReadStorage<'a, C>,
|
storage: &ReadStorage<'a, C>,
|
||||||
filter: impl Join + Copy,
|
filter: impl Join + Copy,
|
||||||
@ -136,13 +144,7 @@ impl<P: CompPacket> SyncPackage<P> {
|
|||||||
P::Phantom: TryInto<PhantomData<C>>,
|
P::Phantom: TryInto<PhantomData<C>>,
|
||||||
C::Storage: specs::storage::Tracked,
|
C::Storage: specs::storage::Tracked,
|
||||||
{
|
{
|
||||||
tracker.get_updates_for(
|
tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
|
||||||
uids,
|
|
||||||
storage,
|
|
||||||
// Don't include updates for deleted entities
|
|
||||||
(filter, &!uid_tracker.removed()),
|
|
||||||
&mut self.comp_updates,
|
|
||||||
);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use super::{
|
|||||||
track::UpdateTracker,
|
track::UpdateTracker,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
|
use log::error;
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::{MarkedBuilder, MarkerAllocator},
|
saveload::{MarkedBuilder, MarkerAllocator},
|
||||||
world::Builder,
|
world::Builder,
|
||||||
@ -20,6 +21,7 @@ pub trait WorldSyncExt {
|
|||||||
where
|
where
|
||||||
C::Storage: Default + specs::storage::Tracked;
|
C::Storage: Default + specs::storage::Tracked;
|
||||||
fn create_entity_synced(&mut self) -> specs::EntityBuilder;
|
fn create_entity_synced(&mut self) -> specs::EntityBuilder;
|
||||||
|
fn delete_entity_and_clear_from_uid_allocator(&mut self, uid: u64);
|
||||||
fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid>;
|
fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid>;
|
||||||
fn entity_from_uid(&self, uid: u64) -> Option<specs::Entity>;
|
fn entity_from_uid(&self, uid: u64) -> Option<specs::Entity>;
|
||||||
fn apply_entity_package<P: CompPacket>(&mut self, entity_package: EntityPackage<P>);
|
fn apply_entity_package<P: CompPacket>(&mut self, entity_package: EntityPackage<P>);
|
||||||
@ -35,14 +37,11 @@ impl WorldSyncExt for specs::World {
|
|||||||
fn register_sync_marker(&mut self) {
|
fn register_sync_marker(&mut self) {
|
||||||
self.register_synced::<Uid>();
|
self.register_synced::<Uid>();
|
||||||
|
|
||||||
|
// TODO: Consider only having allocator server side for now
|
||||||
self.add_resource(UidAllocator::new());
|
self.add_resource(UidAllocator::new());
|
||||||
}
|
}
|
||||||
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
|
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||||
where
|
where
|
||||||
// P: From<C>,
|
|
||||||
// C: TryFrom<P, Error = InvalidType>,
|
|
||||||
// P::Phantom: From<PhantomData<C>>,
|
|
||||||
// P::Phantom: TryInto<PhantomData<C>>,
|
|
||||||
C::Storage: Default + specs::storage::Tracked,
|
C::Storage: Default + specs::storage::Tracked,
|
||||||
{
|
{
|
||||||
self.register::<C>();
|
self.register::<C>();
|
||||||
@ -50,26 +49,12 @@ impl WorldSyncExt for specs::World {
|
|||||||
}
|
}
|
||||||
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
|
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||||
where
|
where
|
||||||
// P: From<C>,
|
|
||||||
// C: TryFrom<P, Error = InvalidType>,
|
|
||||||
// P::Phantom: From<PhantomData<C>>,
|
|
||||||
// P::Phantom: TryInto<PhantomData<C>>,
|
|
||||||
C::Storage: Default + specs::storage::Tracked,
|
C::Storage: Default + specs::storage::Tracked,
|
||||||
{
|
{
|
||||||
let tracker = UpdateTracker::<C>::new(self);
|
let tracker = UpdateTracker::<C>::new(self);
|
||||||
self.add_resource(tracker);
|
self.add_resource(tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn insert_synced<C: specs::shred::Resource + Clone + Send + Sync>(&mut self, res: C)
|
|
||||||
//where
|
|
||||||
// R: From<C>,
|
|
||||||
// C: TryFrom<R>,
|
|
||||||
{
|
|
||||||
self.add_resource::<C>(res);
|
|
||||||
|
|
||||||
self.res_trackers.insert(ResUpdateTracker::<C>::new());
|
|
||||||
}*/
|
|
||||||
|
|
||||||
fn create_entity_synced(&mut self) -> specs::EntityBuilder {
|
fn create_entity_synced(&mut self) -> specs::EntityBuilder {
|
||||||
self.create_entity().marked::<super::Uid>()
|
self.create_entity().marked::<super::Uid>()
|
||||||
}
|
}
|
||||||
@ -94,6 +79,16 @@ impl WorldSyncExt for specs::World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn delete_entity_and_clear_from_uid_allocator(&mut self, uid: u64) {
|
||||||
|
// Clear from uid allocator
|
||||||
|
let maybe_entity = self.write_resource::<UidAllocator>().remove_entity(uid);
|
||||||
|
if let Some(entity) = maybe_entity {
|
||||||
|
if let Err(err) = self.delete_entity(entity) {
|
||||||
|
error!("Failed to delete entity: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_state_package<P: CompPacket, R: ResPacket>(
|
fn apply_state_package<P: CompPacket, R: ResPacket>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state_package: StatePackage<P, R>,
|
state_package: StatePackage<P, R>,
|
||||||
@ -114,7 +109,7 @@ impl WorldSyncExt for specs::World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize entities
|
// Initialize entities
|
||||||
//specs_world.maintain();
|
//self.maintain();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>) {
|
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>) {
|
||||||
@ -152,12 +147,7 @@ impl WorldSyncExt for specs::World {
|
|||||||
|
|
||||||
// Attempt to delete entities that were marked for deletion
|
// Attempt to delete entities that were marked for deletion
|
||||||
for entity_uid in deleted_entities {
|
for entity_uid in deleted_entities {
|
||||||
let entity = self
|
self.delete_entity_and_clear_from_uid_allocator(entity_uid);
|
||||||
.read_resource::<UidAllocator>()
|
|
||||||
.retrieve_entity_internal(entity_uid);
|
|
||||||
if let Some(entity) = entity {
|
|
||||||
let _ = self.delete_entity(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn apply_res_sync_package<R: ResPacket>(&mut self, package: ResSyncPackage<R>) {
|
fn apply_res_sync_package<R: ResPacket>(&mut self, package: ResSyncPackage<R>) {
|
||||||
@ -174,5 +164,15 @@ fn create_entity_with_uid(specs_world: &mut specs::World, entity_uid: u64) -> sp
|
|||||||
.read_resource::<UidAllocator>()
|
.read_resource::<UidAllocator>()
|
||||||
.retrieve_entity_internal(entity_uid);
|
.retrieve_entity_internal(entity_uid);
|
||||||
|
|
||||||
existing_entity.unwrap_or_else(|| specs_world.create_entity_synced().build())
|
match existing_entity {
|
||||||
|
Some(entity) => entity,
|
||||||
|
None => {
|
||||||
|
let entity_builder = specs_world.create_entity();
|
||||||
|
let uid = entity_builder
|
||||||
|
.world
|
||||||
|
.write_resource::<UidAllocator>()
|
||||||
|
.allocate(entity_builder.entity, Some(entity_uid));
|
||||||
|
entity_builder.with(uid).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ where
|
|||||||
specs::storage::ComponentEvent::Modified(id) => {
|
specs::storage::ComponentEvent::Modified(id) => {
|
||||||
// We don't care about modification if the component was just added or was
|
// We don't care about modification if the component was just added or was
|
||||||
// removed
|
// removed
|
||||||
|
// Could potentially remove since this should theoretically never occur...
|
||||||
if !self.removed.contains(*id) && !self.inserted.contains(*id) {
|
if !self.removed.contains(*id) && !self.inserted.contains(*id) {
|
||||||
self.modified.add(*id);
|
self.modified.add(*id);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,10 @@ impl UidAllocator {
|
|||||||
mapping: HashMap::new(),
|
mapping: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Useful for when a single entity is deleted because it doesn't reconstruct the entire hashmap
|
||||||
|
pub fn remove_entity(&mut self, id: u64) -> Option<Entity> {
|
||||||
|
self.mapping.remove(&id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UidAllocator {
|
impl Default for UidAllocator {
|
||||||
@ -69,14 +73,14 @@ impl MarkerAllocator<Uid> for UidAllocator {
|
|||||||
let id = id.unwrap_or_else(|| {
|
let id = id.unwrap_or_else(|| {
|
||||||
let id = self.index;
|
let id = self.index;
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
self.mapping.insert(id, entity);
|
|
||||||
id
|
id
|
||||||
});
|
});
|
||||||
|
self.mapping.insert(id, entity);
|
||||||
Uid(id)
|
Uid(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
|
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
|
||||||
self.mapping.get(&id).cloned()
|
self.mapping.get(&id).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<Uid>) {
|
fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<Uid>) {
|
||||||
|
@ -21,6 +21,7 @@ use vek::*;
|
|||||||
use world::util::Sampler;
|
use world::util::Sampler;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use log::error;
|
||||||
use scan_fmt::{scan_fmt, scan_fmt_some};
|
use scan_fmt::{scan_fmt, scan_fmt_some};
|
||||||
|
|
||||||
/// Struct representing a command that a user can run from server chat.
|
/// Struct representing a command that a user can run from server chat.
|
||||||
@ -1118,7 +1119,9 @@ fn handle_remove_lights(
|
|||||||
let size = to_delete.len();
|
let size = to_delete.len();
|
||||||
|
|
||||||
for entity in to_delete {
|
for entity in to_delete {
|
||||||
let _ = server.state.ecs_mut().delete_entity(entity);
|
if let Err(err) = server.state.delete_entity_recorded(entity) {
|
||||||
|
error!("Failed to delete light: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
|
@ -19,7 +19,7 @@ use crate::{
|
|||||||
chunk_generator::ChunkGenerator,
|
chunk_generator::ChunkGenerator,
|
||||||
client::{Client, RegionSubscription},
|
client::{Client, RegionSubscription},
|
||||||
cmd::CHAT_COMMANDS,
|
cmd::CHAT_COMMANDS,
|
||||||
sys::sentinel::{TrackedComps, TrackedResources},
|
sys::sentinel::{DeletedEntities, TrackedComps, TrackedResources},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets, comp,
|
assets, comp,
|
||||||
@ -32,7 +32,7 @@ use common::{
|
|||||||
terrain::{block::Block, TerrainChunkSize, TerrainGrid},
|
terrain::{block::Block, TerrainChunkSize, TerrainGrid},
|
||||||
vol::{ReadVol, RectVolSize, Vox},
|
vol::{ReadVol, RectVolSize, Vox},
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::{debug, error};
|
||||||
use metrics::ServerMetrics;
|
use metrics::ServerMetrics;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{
|
use specs::{
|
||||||
@ -166,6 +166,8 @@ impl Server {
|
|||||||
// Register trackers
|
// Register trackers
|
||||||
sys::sentinel::register_trackers(&mut state.ecs_mut());
|
sys::sentinel::register_trackers(&mut state.ecs_mut());
|
||||||
|
|
||||||
|
state.ecs_mut().add_resource(DeletedEntities::default());
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
state,
|
state,
|
||||||
world: Arc::new(world),
|
world: Arc::new(world),
|
||||||
@ -309,8 +311,6 @@ impl Server {
|
|||||||
|
|
||||||
let server_settings = &self.server_settings;
|
let server_settings = &self.server_settings;
|
||||||
|
|
||||||
let mut todo_remove = None;
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
ServerEvent::Explosion { pos, radius } => {
|
ServerEvent::Explosion { pos, radius } => {
|
||||||
const RAYS: usize = 500;
|
const RAYS: usize = 500;
|
||||||
@ -380,19 +380,20 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerEvent::Destroy { entity, cause } => {
|
ServerEvent::Destroy { entity, cause } => {
|
||||||
let ecs = state.ecs();
|
|
||||||
// Chat message
|
// Chat message
|
||||||
if let Some(player) = ecs.read_storage::<comp::Player>().get(entity) {
|
if let Some(player) = state.ecs().read_storage::<comp::Player>().get(entity) {
|
||||||
let msg = if let comp::HealthSource::Attack { by } = cause {
|
let msg = if let comp::HealthSource::Attack { by } = cause {
|
||||||
ecs.entity_from_uid(by.into()).and_then(|attacker| {
|
state.ecs().entity_from_uid(by.into()).and_then(|attacker| {
|
||||||
ecs.read_storage::<comp::Player>().get(attacker).map(
|
state
|
||||||
|attacker_alias| {
|
.ecs()
|
||||||
|
.read_storage::<comp::Player>()
|
||||||
|
.get(attacker)
|
||||||
|
.map(|attacker_alias| {
|
||||||
format!(
|
format!(
|
||||||
"{} was killed by {}",
|
"{} was killed by {}",
|
||||||
&player.alias, &attacker_alias.alias
|
&player.alias, &attacker_alias.alias
|
||||||
)
|
)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -402,28 +403,44 @@ impl Server {
|
|||||||
state.notify_registered_clients(ServerMsg::kill(msg));
|
state.notify_registered_clients(ServerMsg::kill(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give EXP to the killer if entity had stats
|
{
|
||||||
let mut stats = ecs.write_storage::<comp::Stats>();
|
// Give EXP to the killer if entity had stats
|
||||||
|
let mut stats = state.ecs().write_storage::<comp::Stats>();
|
||||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||||
if let comp::HealthSource::Attack { by } = cause {
|
if let comp::HealthSource::Attack { by } = cause {
|
||||||
ecs.entity_from_uid(by.into()).map(|attacker| {
|
state.ecs().entity_from_uid(by.into()).map(|attacker| {
|
||||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||||
attacker_stats
|
attacker_stats
|
||||||
.exp
|
.exp
|
||||||
.change_by((entity_stats.level.level() * 10) as i64);
|
.change_by((entity_stats.level.level() * 10) as i64);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(client) = ecs.write_storage::<Client>().get_mut(entity) {
|
// This sucks
|
||||||
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
|
let mut remove = false;
|
||||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
|
||||||
|
if let Some(client) = state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||||
|
let _ = state
|
||||||
|
.ecs()
|
||||||
|
.write_storage()
|
||||||
|
.insert(entity, comp::Vel(Vec3::zero()));
|
||||||
|
let _ = state
|
||||||
|
.ecs()
|
||||||
|
.write_storage()
|
||||||
|
.insert(entity, comp::ForceUpdate);
|
||||||
client.force_state(ClientState::Dead);
|
client.force_state(ClientState::Dead);
|
||||||
} else {
|
} else {
|
||||||
todo_remove = Some(entity.clone());
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if remove {
|
||||||
|
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||||
|
error!("Failed to delete destroyed entity: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +474,9 @@ impl Server {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(item_entity) = item_entity {
|
if let Some(item_entity) = item_entity {
|
||||||
let _ = state.ecs_mut().delete_entity(item_entity);
|
if let Err(err) = state.delete_entity_recorded(item_entity) {
|
||||||
|
error!("Failed to delete picked up item entity: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.write_component(entity, comp::InventoryUpdate);
|
state.write_component(entity, comp::InventoryUpdate);
|
||||||
@ -728,7 +747,7 @@ impl Server {
|
|||||||
main,
|
main,
|
||||||
&server_settings,
|
&server_settings,
|
||||||
);
|
);
|
||||||
Self::initialize_region_subscription(state, entity);
|
sys::subscription::initialize_region_subscription(state.ecs(), entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerEvent::CreateNpc {
|
ServerEvent::CreateNpc {
|
||||||
@ -746,8 +765,8 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerEvent::ClientDisconnect(entity) => {
|
ServerEvent::ClientDisconnect(entity) => {
|
||||||
if let Err(err) = state.ecs_mut().delete_entity(entity) {
|
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||||
debug!("Failed to delete disconnected client: {:?}", err);
|
error!("Failed to delete disconnected client: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
frontend_events.push(Event::ClientDisconnected { entity });
|
frontend_events.push(Event::ClientDisconnected { entity });
|
||||||
@ -761,11 +780,6 @@ impl Server {
|
|||||||
chat_commands.push((entity, cmd));
|
chat_commands.push((entity, cmd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this needed?
|
|
||||||
if let Some(entity) = todo_remove {
|
|
||||||
let _ = state.ecs_mut().delete_entity(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate requested chunks.
|
// Generate requested chunks.
|
||||||
@ -825,7 +839,7 @@ impl Server {
|
|||||||
frontend_events.append(&mut self.handle_new_connections()?);
|
frontend_events.append(&mut self.handle_new_connections()?);
|
||||||
|
|
||||||
let before_tick_4 = Instant::now();
|
let before_tick_4 = Instant::now();
|
||||||
// 4) Tick the client's LocalState.
|
// 4) Tick the server's LocalState.
|
||||||
self.state.tick(dt, sys::add_server_systems);
|
self.state.tick(dt, sys::add_server_systems);
|
||||||
|
|
||||||
let before_handle_events = Instant::now();
|
let before_handle_events = Instant::now();
|
||||||
@ -856,7 +870,9 @@ impl Server {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
for entity in to_delete {
|
for entity in to_delete {
|
||||||
let _ = self.state.ecs_mut().delete_entity(entity);
|
if let Err(err) = self.state.delete_entity_recorded(entity) {
|
||||||
|
error!("Failed to delete agent outside the terrain: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let before_tick_7 = Instant::now();
|
let before_tick_7 = Instant::now();
|
||||||
@ -1004,83 +1020,6 @@ impl Server {
|
|||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize region subscription
|
|
||||||
fn initialize_region_subscription(state: &mut State, entity: specs::Entity) {
|
|
||||||
let mut subscription = None;
|
|
||||||
|
|
||||||
if let (Some(client_pos), Some(client_vd), Some(client)) = (
|
|
||||||
state.ecs().read_storage::<comp::Pos>().get(entity),
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<comp::Player>()
|
|
||||||
.get(entity)
|
|
||||||
.map(|pl| pl.view_distance)
|
|
||||||
.and_then(|v| v),
|
|
||||||
state.ecs().write_storage::<Client>().get_mut(entity),
|
|
||||||
) {
|
|
||||||
use common::region::RegionMap;
|
|
||||||
|
|
||||||
let fuzzy_chunk = (Vec2::<f32>::from(client_pos.0))
|
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
|
||||||
let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
|
||||||
let regions = common::region::regions_in_vd(
|
|
||||||
client_pos.0,
|
|
||||||
(client_vd as f32 * chunk_size) as f32
|
|
||||||
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (_, region) in state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<RegionMap>()
|
|
||||||
.iter()
|
|
||||||
.filter(|(key, _)| regions.contains(key))
|
|
||||||
{
|
|
||||||
// Sync physics of all entities in this region
|
|
||||||
for (&uid, &pos, vel, ori, character_state, _) in (
|
|
||||||
&state.ecs().read_storage::<Uid>(),
|
|
||||||
&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::CharacterState>().maybe(),
|
|
||||||
region.entities(),
|
|
||||||
)
|
|
||||||
.join()
|
|
||||||
{
|
|
||||||
client.notify(ServerMsg::EntityPos {
|
|
||||||
entity: uid.into(),
|
|
||||||
pos,
|
|
||||||
});
|
|
||||||
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(character_state) = character_state.copied() {
|
|
||||||
client.notify(ServerMsg::EntityCharacterState {
|
|
||||||
entity: uid.into(),
|
|
||||||
character_state,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription = Some(RegionSubscription {
|
|
||||||
fuzzy_chunk,
|
|
||||||
regions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if let Some(subscription) = subscription {
|
|
||||||
state.write_component(entity, subscription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn notify_client(&self, entity: EcsEntity, msg: ServerMsg) {
|
pub fn notify_client(&self, entity: EcsEntity, msg: ServerMsg) {
|
||||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||||
client.notify(msg)
|
client.notify(msg)
|
||||||
@ -1142,6 +1081,10 @@ trait StateExt {
|
|||||||
stats: comp::Stats,
|
stats: comp::Stats,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
|
fn delete_entity_recorded(
|
||||||
|
&mut self,
|
||||||
|
entity: EcsEntity,
|
||||||
|
) -> Result<(), specs::error::WrongGeneration>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateExt for State {
|
impl StateExt for State {
|
||||||
@ -1202,4 +1145,28 @@ impl StateExt for State {
|
|||||||
client.notify(msg.clone())
|
client.notify(msg.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn delete_entity_recorded(
|
||||||
|
&mut self,
|
||||||
|
entity: EcsEntity,
|
||||||
|
) -> Result<(), specs::error::WrongGeneration> {
|
||||||
|
let (maybe_uid, maybe_pos) = (
|
||||||
|
self.ecs().read_storage::<Uid>().get(entity).copied(),
|
||||||
|
self.ecs().read_storage::<comp::Pos>().get(entity).copied(),
|
||||||
|
);
|
||||||
|
let res = self.ecs_mut().delete_entity(entity);
|
||||||
|
if res.is_ok() {
|
||||||
|
if let (Some(uid), Some(pos)) = (maybe_uid, maybe_pos) {
|
||||||
|
let region_key = self
|
||||||
|
.ecs()
|
||||||
|
.read_resource::<common::region::RegionMap>()
|
||||||
|
.find_region(entity, pos.0)
|
||||||
|
.expect("Failed to find region containing entity during entity deletion");
|
||||||
|
self.ecs()
|
||||||
|
.write_resource::<DeletedEntities>()
|
||||||
|
.record_deleted_entity(uid, region_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
sentinel::{ReadTrackers, TrackedComps, TrackedResources},
|
sentinel::{DeletedEntities, ReadTrackers, TrackedComps, TrackedResources},
|
||||||
SysTimer,
|
SysTimer,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -38,6 +38,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, Client>,
|
WriteStorage<'a, Client>,
|
||||||
WriteStorage<'a, ForceUpdate>,
|
WriteStorage<'a, ForceUpdate>,
|
||||||
WriteStorage<'a, InventoryUpdate>,
|
WriteStorage<'a, InventoryUpdate>,
|
||||||
|
Write<'a, DeletedEntities>,
|
||||||
TrackedComps<'a>,
|
TrackedComps<'a>,
|
||||||
ReadTrackers<'a>,
|
ReadTrackers<'a>,
|
||||||
TrackedResources<'a>,
|
TrackedResources<'a>,
|
||||||
@ -64,8 +65,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut clients,
|
mut clients,
|
||||||
mut force_updates,
|
mut force_updates,
|
||||||
mut inventory_updates,
|
mut inventory_updates,
|
||||||
|
mut deleted_entities,
|
||||||
tracked_comps,
|
tracked_comps,
|
||||||
read_trackers,
|
trackers,
|
||||||
tracked_resources,
|
tracked_resources,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
@ -86,6 +88,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Sync physics
|
// Sync physics
|
||||||
// via iterating through regions
|
// via iterating through regions
|
||||||
for (key, region) in region_map.iter() {
|
for (key, region) in region_map.iter() {
|
||||||
|
// Assemble subscriber list for this region by iterating through clients and checking
|
||||||
|
// if they are subscribed to this region
|
||||||
let mut subscribers = (&mut clients, &entities, &subscriptions, &positions)
|
let mut subscribers = (&mut clients, &entities, &subscriptions, &positions)
|
||||||
.join()
|
.join()
|
||||||
.filter_map(|(client, entity, subscription, pos)| {
|
.filter_map(|(client, entity, subscription, pos)| {
|
||||||
@ -100,6 +104,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
for event in region.events() {
|
for event in region.events() {
|
||||||
match event {
|
match event {
|
||||||
RegionEvent::Entered(id, maybe_key) => {
|
RegionEvent::Entered(id, maybe_key) => {
|
||||||
|
// Don't process newly created entities here (redundant network messages)
|
||||||
|
if trackers.uid.inserted().contains(*id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let entity = entities.entity(*id);
|
let entity = entities.entity(*id);
|
||||||
if let Some((uid, pos, vel, ori, character_state)) =
|
if let Some((uid, pos, vel, ori, character_state)) =
|
||||||
uids.get(entity).and_then(|uid| {
|
uids.get(entity).and_then(|uid| {
|
||||||
@ -156,8 +164,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sync tracked components
|
// Sync tracked components
|
||||||
|
// Get deleted entities in this region from DeletedEntities
|
||||||
let sync_msg = ServerMsg::EcsSync(
|
let sync_msg = ServerMsg::EcsSync(
|
||||||
read_trackers.create_sync_package(&tracked_comps, region.entities()),
|
trackers.create_sync_package(
|
||||||
|
&tracked_comps,
|
||||||
|
region.entities(),
|
||||||
|
deleted_entities
|
||||||
|
.take_deleted_in_region(key)
|
||||||
|
.unwrap_or_else(|| Vec::new()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
for (client, _, _, _) in &mut subscribers {
|
for (client, _, _, _) in &mut subscribers {
|
||||||
client.notify(sync_msg.clone());
|
client.notify(sync_msg.clone());
|
||||||
@ -169,9 +184,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
force_update: Option<&ForceUpdate>,
|
force_update: Option<&ForceUpdate>,
|
||||||
throttle: bool| {
|
throttle: bool| {
|
||||||
for (client, _, client_entity, client_pos) in &mut subscribers {
|
for (client, _, client_entity, client_pos) in &mut subscribers {
|
||||||
let update = if client_entity == &entity && force_update.is_none() {
|
let update = if client_entity == &entity {
|
||||||
// Don't send client physics update about itself
|
// Don't send client physics updates about itself unless force update is set
|
||||||
false
|
force_update.is_some()
|
||||||
} else if !throttle {
|
} else if !throttle {
|
||||||
// Update rate not thottled by distance
|
// Update rate not thottled by distance
|
||||||
true
|
true
|
||||||
@ -283,6 +298,25 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle entity deletion in regions that don't exist in RegionMap (theoretically none)
|
||||||
|
for (region_key, deleted) in deleted_entities.take_remaining_deleted() {
|
||||||
|
for client in
|
||||||
|
(&mut clients, &subscriptions)
|
||||||
|
.join()
|
||||||
|
.filter_map(|(client, subscription)| {
|
||||||
|
if client.is_ingame() && subscription.regions.contains(®ion_key) {
|
||||||
|
Some(client)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
for uid in &deleted {
|
||||||
|
client.notify(ServerMsg::DeleteEntity(*uid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Sync clients that don't have a position?
|
// TODO: Sync clients that don't have a position?
|
||||||
|
|
||||||
// Sync inventories
|
// Sync inventories
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::SysTimer;
|
use super::SysTimer;
|
||||||
use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
|
use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Admin, Body, CanBuild, Controller, Ori, Player, Pos, Vel},
|
comp::{Admin, Body, CanBuild, Controller, ForceUpdate, Ori, Player, Pos, Vel},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{validate_chat_msg, ChatMsgValidationError, MAX_BYTES_CHAT_MSG},
|
msg::{validate_chat_msg, ChatMsgValidationError, MAX_BYTES_CHAT_MSG},
|
||||||
msg::{ClientMsg, ClientState, RequestStateError, ServerMsg},
|
msg::{ClientMsg, ClientState, RequestStateError, ServerMsg},
|
||||||
@ -25,6 +25,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Body>,
|
ReadStorage<'a, Body>,
|
||||||
ReadStorage<'a, CanBuild>,
|
ReadStorage<'a, CanBuild>,
|
||||||
ReadStorage<'a, Admin>,
|
ReadStorage<'a, Admin>,
|
||||||
|
ReadStorage<'a, ForceUpdate>,
|
||||||
WriteExpect<'a, AuthProvider>,
|
WriteExpect<'a, AuthProvider>,
|
||||||
Write<'a, BlockChange>,
|
Write<'a, BlockChange>,
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
bodies,
|
bodies,
|
||||||
can_build,
|
can_build,
|
||||||
admins,
|
admins,
|
||||||
|
force_updates,
|
||||||
mut accounts,
|
mut accounts,
|
||||||
mut block_changes,
|
mut block_changes,
|
||||||
mut positions,
|
mut positions,
|
||||||
@ -218,9 +220,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
},
|
},
|
||||||
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
||||||
ClientState::Character => {
|
ClientState::Character => {
|
||||||
let _ = positions.insert(entity, pos);
|
if force_updates.get(entity).is_none() {
|
||||||
let _ = velocities.insert(entity, vel);
|
let _ = positions.insert(entity, pos);
|
||||||
let _ = orientations.insert(entity, ori);
|
let _ = velocities.insert(entity, vel);
|
||||||
|
let _ = orientations.insert(entity, ori);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Only characters can send positions.
|
// Only characters can send positions.
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
|
@ -5,17 +5,19 @@ use common::{
|
|||||||
Projectile, Scale, Stats, Sticky,
|
Projectile, Scale, Stats, Sticky,
|
||||||
},
|
},
|
||||||
msg::{EcsCompPacket, EcsResPacket},
|
msg::{EcsCompPacket, EcsResPacket},
|
||||||
state::{Time, TimeOfDay},
|
state::TimeOfDay,
|
||||||
sync::{
|
sync::{
|
||||||
CompPacket, EntityPackage, ResSyncPackage, StatePackage, SyncPackage, Uid, UpdateTracker,
|
CompPacket, EntityPackage, ResSyncPackage, StatePackage, SyncPackage, Uid, UpdateTracker,
|
||||||
WorldSyncExt,
|
WorldSyncExt,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use shred_derive::SystemData;
|
use shred_derive::SystemData;
|
||||||
use specs::{
|
use specs::{
|
||||||
Entity as EcsEntity, Join, ReadExpect, ReadStorage, System, World, Write, WriteExpect,
|
Entity as EcsEntity, Join, ReadExpect, ReadStorage, System, World, Write, WriteExpect,
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
/// Always watching
|
/// Always watching
|
||||||
/// This system will monitor specific components for insertion, removal, and modification
|
/// This system will monitor specific components for insertion, removal, and modification
|
||||||
@ -39,20 +41,20 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Probably more difficult than it needs to be :p
|
// Probably more difficult than it needs to be :p
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct TrackedComps<'a> {
|
pub struct TrackedComps<'a> {
|
||||||
uid: ReadStorage<'a, Uid>,
|
pub uid: ReadStorage<'a, Uid>,
|
||||||
body: ReadStorage<'a, Body>,
|
pub body: ReadStorage<'a, Body>,
|
||||||
player: ReadStorage<'a, Player>,
|
pub player: ReadStorage<'a, Player>,
|
||||||
stats: ReadStorage<'a, Stats>,
|
pub stats: ReadStorage<'a, Stats>,
|
||||||
can_build: ReadStorage<'a, CanBuild>,
|
pub can_build: ReadStorage<'a, CanBuild>,
|
||||||
light_emitter: ReadStorage<'a, LightEmitter>,
|
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||||
item: ReadStorage<'a, Item>,
|
pub item: ReadStorage<'a, Item>,
|
||||||
scale: ReadStorage<'a, Scale>,
|
pub scale: ReadStorage<'a, Scale>,
|
||||||
mounting: ReadStorage<'a, Mounting>,
|
pub mounting: ReadStorage<'a, Mounting>,
|
||||||
mount_state: ReadStorage<'a, MountState>,
|
pub mount_state: ReadStorage<'a, MountState>,
|
||||||
mass: ReadStorage<'a, Mass>,
|
pub mass: ReadStorage<'a, Mass>,
|
||||||
sticky: ReadStorage<'a, Sticky>,
|
pub sticky: ReadStorage<'a, Sticky>,
|
||||||
gravity: ReadStorage<'a, Gravity>,
|
pub gravity: ReadStorage<'a, Gravity>,
|
||||||
projectile: ReadStorage<'a, Projectile>,
|
pub projectile: ReadStorage<'a, Projectile>,
|
||||||
}
|
}
|
||||||
impl<'a> TrackedComps<'a> {
|
impl<'a> TrackedComps<'a> {
|
||||||
pub fn create_entity_package(&self, entity: EcsEntity) -> EntityPackage<EcsCompPacket> {
|
pub fn create_entity_package(&self, entity: EcsEntity) -> EntityPackage<EcsCompPacket> {
|
||||||
@ -121,115 +123,53 @@ impl<'a> TrackedComps<'a> {
|
|||||||
}
|
}
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadTrackers<'a> {
|
pub struct ReadTrackers<'a> {
|
||||||
uid: ReadExpect<'a, UpdateTracker<Uid>>,
|
pub uid: ReadExpect<'a, UpdateTracker<Uid>>,
|
||||||
body: ReadExpect<'a, UpdateTracker<Body>>,
|
pub body: ReadExpect<'a, UpdateTracker<Body>>,
|
||||||
player: ReadExpect<'a, UpdateTracker<Player>>,
|
pub player: ReadExpect<'a, UpdateTracker<Player>>,
|
||||||
stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
||||||
can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
|
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
|
||||||
light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
|
pub light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
|
||||||
item: ReadExpect<'a, UpdateTracker<Item>>,
|
pub item: ReadExpect<'a, UpdateTracker<Item>>,
|
||||||
scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
||||||
mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
pub mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
||||||
mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||||
mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||||
sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
||||||
gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
||||||
projectile: ReadExpect<'a, UpdateTracker<Projectile>>,
|
pub projectile: ReadExpect<'a, UpdateTracker<Projectile>>,
|
||||||
}
|
}
|
||||||
impl<'a> ReadTrackers<'a> {
|
impl<'a> ReadTrackers<'a> {
|
||||||
pub fn create_sync_package(
|
pub fn create_sync_package(
|
||||||
&self,
|
&self,
|
||||||
comps: &TrackedComps,
|
comps: &TrackedComps,
|
||||||
filter: impl Join + Copy,
|
filter: impl Join + Copy,
|
||||||
|
deleted_entities: Vec<u64>,
|
||||||
) -> SyncPackage<EcsCompPacket> {
|
) -> SyncPackage<EcsCompPacket> {
|
||||||
SyncPackage::new(&comps.uid, &self.uid, filter)
|
SyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities)
|
||||||
|
.with_component(&comps.uid, self.body.deref(), &comps.body, filter)
|
||||||
|
.with_component(&comps.uid, self.player.deref(), &comps.player, filter)
|
||||||
|
.with_component(&comps.uid, self.stats.deref(), &comps.stats, filter)
|
||||||
|
.with_component(&comps.uid, self.can_build.deref(), &comps.can_build, filter)
|
||||||
.with_component(
|
.with_component(
|
||||||
&comps.uid,
|
&comps.uid,
|
||||||
&self.uid,
|
|
||||||
self.body.deref(),
|
|
||||||
&comps.body,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.player.deref(),
|
|
||||||
&comps.player,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.stats.deref(),
|
|
||||||
&comps.stats,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.can_build.deref(),
|
|
||||||
&comps.can_build,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.light_emitter.deref(),
|
self.light_emitter.deref(),
|
||||||
&comps.light_emitter,
|
&comps.light_emitter,
|
||||||
filter,
|
filter,
|
||||||
)
|
)
|
||||||
|
.with_component(&comps.uid, self.item.deref(), &comps.item, filter)
|
||||||
|
.with_component(&comps.uid, self.scale.deref(), &comps.scale, filter)
|
||||||
|
.with_component(&comps.uid, self.mounting.deref(), &comps.mounting, filter)
|
||||||
.with_component(
|
.with_component(
|
||||||
&comps.uid,
|
&comps.uid,
|
||||||
&self.uid,
|
|
||||||
self.item.deref(),
|
|
||||||
&comps.item,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.scale.deref(),
|
|
||||||
&comps.scale,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.mounting.deref(),
|
|
||||||
&comps.mounting,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.mount_state.deref(),
|
self.mount_state.deref(),
|
||||||
&comps.mount_state,
|
&comps.mount_state,
|
||||||
filter,
|
filter,
|
||||||
)
|
)
|
||||||
|
.with_component(&comps.uid, self.mass.deref(), &comps.mass, filter)
|
||||||
|
.with_component(&comps.uid, self.sticky.deref(), &comps.sticky, filter)
|
||||||
|
.with_component(&comps.uid, self.gravity.deref(), &comps.gravity, filter)
|
||||||
.with_component(
|
.with_component(
|
||||||
&comps.uid,
|
&comps.uid,
|
||||||
&self.uid,
|
|
||||||
self.mass.deref(),
|
|
||||||
&comps.mass,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.sticky.deref(),
|
|
||||||
&comps.sticky,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.gravity.deref(),
|
|
||||||
&comps.gravity,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
.with_component(
|
|
||||||
&comps.uid,
|
|
||||||
&self.uid,
|
|
||||||
self.projectile.deref(),
|
self.projectile.deref(),
|
||||||
&comps.projectile,
|
&comps.projectile,
|
||||||
filter,
|
filter,
|
||||||
@ -292,19 +232,46 @@ pub fn register_trackers(world: &mut World) {
|
|||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct TrackedResources<'a> {
|
pub struct TrackedResources<'a> {
|
||||||
time: ReadExpect<'a, Time>,
|
|
||||||
time_of_day: ReadExpect<'a, TimeOfDay>,
|
time_of_day: ReadExpect<'a, TimeOfDay>,
|
||||||
}
|
}
|
||||||
impl<'a> TrackedResources<'a> {
|
impl<'a> TrackedResources<'a> {
|
||||||
pub fn create_res_sync_package(&self) -> ResSyncPackage<EcsResPacket> {
|
pub fn create_res_sync_package(&self) -> ResSyncPackage<EcsResPacket> {
|
||||||
ResSyncPackage::new()
|
ResSyncPackage::new().with_res(self.time_of_day.deref())
|
||||||
.with_res(self.time.deref())
|
|
||||||
.with_res(self.time_of_day.deref())
|
|
||||||
}
|
}
|
||||||
/// Create state package with resources included
|
/// Create state package with resources included
|
||||||
pub fn state_package<C: CompPacket>(&self) -> StatePackage<C, EcsResPacket> {
|
pub fn state_package<C: CompPacket>(&self) -> StatePackage<C, EcsResPacket> {
|
||||||
StatePackage::new()
|
StatePackage::new().with_res(self.time_of_day.deref())
|
||||||
.with_res(self.time.deref())
|
}
|
||||||
.with_res(self.time_of_day.deref())
|
}
|
||||||
|
|
||||||
|
/// Deleted entities grouped by region
|
||||||
|
pub struct DeletedEntities {
|
||||||
|
map: HashMap<Vec2<i32>, Vec<u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DeletedEntities {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
map: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeletedEntities {
|
||||||
|
pub fn record_deleted_entity(&mut self, uid: Uid, region_key: Vec2<i32>) {
|
||||||
|
self.map
|
||||||
|
.entry(region_key)
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push(uid.into());
|
||||||
|
}
|
||||||
|
pub fn take_deleted_in_region(&mut self, key: Vec2<i32>) -> Option<Vec<u64>> {
|
||||||
|
self.map.remove(&key)
|
||||||
|
}
|
||||||
|
pub fn get_deleted_in_region(&mut self, key: Vec2<i32>) -> Option<&Vec<u64>> {
|
||||||
|
self.map.get(&key)
|
||||||
|
}
|
||||||
|
pub fn take_remaining_deleted(&mut self) -> Vec<(Vec2<i32>, Vec<u64>)> {
|
||||||
|
// TODO: don't allocate
|
||||||
|
self.map.drain().collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use super::{sentinel::TrackedComps, SysTimer};
|
use super::{
|
||||||
|
sentinel::{DeletedEntities, TrackedComps},
|
||||||
|
SysTimer,
|
||||||
|
};
|
||||||
use crate::client::{self, Client, RegionSubscription};
|
use crate::client::{self, Client, RegionSubscription};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{CharacterState, Ori, Player, Pos, Vel},
|
comp::{CharacterState, Ori, Player, Pos, Vel},
|
||||||
@ -8,7 +11,10 @@ use common::{
|
|||||||
terrain::TerrainChunkSize,
|
terrain::TerrainChunkSize,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
use log::{debug, error};
|
||||||
|
use specs::{
|
||||||
|
Entities, Join, ReadExpect, ReadStorage, System, SystemData, World, Write, WriteStorage,
|
||||||
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// This system will update region subscriptions based on client positions
|
/// This system will update region subscriptions based on client positions
|
||||||
@ -26,6 +32,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Player>,
|
ReadStorage<'a, Player>,
|
||||||
WriteStorage<'a, Client>,
|
WriteStorage<'a, Client>,
|
||||||
WriteStorage<'a, RegionSubscription>,
|
WriteStorage<'a, RegionSubscription>,
|
||||||
|
Write<'a, DeletedEntities>,
|
||||||
TrackedComps<'a>,
|
TrackedComps<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -43,6 +50,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
players,
|
players,
|
||||||
mut clients,
|
mut clients,
|
||||||
mut subscriptions,
|
mut subscriptions,
|
||||||
|
mut deleted_entities,
|
||||||
tracked_comps,
|
tracked_comps,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
@ -68,8 +76,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
&entities,
|
&entities,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter_map(|(c, s, pos, player, e)| player.view_distance.map(|v| (c, s, pos, v, e)))
|
.filter_map(|(client, s, pos, player, e)| {
|
||||||
|
if client.is_ingame() {
|
||||||
|
player.view_distance.map(|v| (client, s, pos, v, e))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
{
|
{
|
||||||
|
// Calculate current chunk
|
||||||
let chunk = (Vec2::<f32>::from(pos.0))
|
let chunk = (Vec2::<f32>::from(pos.0))
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||||
// Only update regions when moving to a new chunk
|
// Only update regions when moving to a new chunk
|
||||||
@ -87,7 +102,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.reduce_or()
|
.reduce_or()
|
||||||
{
|
{
|
||||||
// Update current chunk
|
// Update current chunk
|
||||||
subscription.fuzzy_chunk = (Vec2::<f32>::from(pos.0))
|
subscription.fuzzy_chunk = dbg!(Vec2::<f32>::from(pos.0))
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||||
// Use the largest side length as our chunk size
|
// Use the largest side length as our chunk size
|
||||||
let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
||||||
@ -108,19 +123,24 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Iterate through regions to remove
|
// Iterate through regions to remove
|
||||||
for key in regions_to_remove.drain(..) {
|
for key in regions_to_remove.drain(..) {
|
||||||
// Remove region from this clients set of subscribed regions
|
// Remove region from this client's set of subscribed regions
|
||||||
subscription.regions.remove(&key);
|
subscription.regions.remove(&key);
|
||||||
// Tell the client to delete the entities in that region if it exists in the RegionMap
|
// Tell the client to delete the entities in that region if it exists in the RegionMap
|
||||||
if let Some(region) = region_map.get(key) {
|
if let Some(region) = region_map.get(key) {
|
||||||
// Process entity left events since they won't be processed during phsyics sync because this region is no longer subscribed to
|
// Process entity left events since they won't be processed during entity sync because this region is no longer subscribed to
|
||||||
|
// TODO: consider changing system ordering??
|
||||||
for event in region.events() {
|
for event in region.events() {
|
||||||
match event {
|
match event {
|
||||||
RegionEvent::Entered(_, _) => {} // These don't need to be processed because this region is being thrown out anyway
|
RegionEvent::Entered(_, _) => {} // These don't need to be processed because this region is being thrown out anyway
|
||||||
RegionEvent::Left(id, maybe_key) => {
|
RegionEvent::Left(id, maybe_key) => {
|
||||||
// Lookup UID for entity
|
// Lookup UID for entity
|
||||||
|
// Doesn't overlap with entity deletion in sync packages
|
||||||
|
// because the uid would not be available if the entity was
|
||||||
|
// deleted
|
||||||
if let Some(&uid) = uids.get(entities.entity(*id)) {
|
if let Some(&uid) = uids.get(entities.entity(*id)) {
|
||||||
if !maybe_key
|
if !maybe_key
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
// Don't need to check that this isn't also in the regions to remove since the entity will be removed when we get to that one
|
||||||
.map(|key| subscription.regions.contains(key))
|
.map(|key| subscription.regions.contains(key))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -130,10 +150,19 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Tell client to delete entities in the region
|
||||||
for (&uid, _) in (&uids, region.entities()).join() {
|
for (&uid, _) in (&uids, region.entities()).join() {
|
||||||
client.notify(ServerMsg::DeleteEntity(uid.into()))
|
client.notify(ServerMsg::DeleteEntity(uid.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Send deleted entities since they won't be processed for this client in entity sync
|
||||||
|
for uid in deleted_entities
|
||||||
|
.get_deleted_in_region(key)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.iter())
|
||||||
|
{
|
||||||
|
client.notify(ServerMsg::DeleteEntity(*uid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in regions_in_vd(
|
for key in regions_in_vd(
|
||||||
@ -141,8 +170,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
(vd as f32 * chunk_size)
|
(vd as f32 * chunk_size)
|
||||||
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||||
) {
|
) {
|
||||||
// Send client intial info about the entities in this region
|
// Send client intial info about the entities in this region if it was not
|
||||||
if subscription.regions.insert(key) {
|
// already within the set of subscribed regions
|
||||||
|
if subscription.regions.insert(key.clone()) {
|
||||||
|
let mut counter = 0;
|
||||||
if let Some(region) = region_map.get(key) {
|
if let Some(region) = region_map.get(key) {
|
||||||
for (uid, pos, vel, ori, character_state, _, entity) in (
|
for (uid, pos, vel, ori, character_state, _, entity) in (
|
||||||
&uids,
|
&uids,
|
||||||
@ -156,6 +187,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.join()
|
.join()
|
||||||
.filter(|(_, _, _, _, _, _, e)| *e != client_entity)
|
.filter(|(_, _, _, _, _, _, e)| *e != client_entity)
|
||||||
{
|
{
|
||||||
|
counter += 1;
|
||||||
// Send message to create entity and tracked components
|
// Send message to create entity and tracked components
|
||||||
client.notify(ServerMsg::CreateEntity(
|
client.notify(ServerMsg::CreateEntity(
|
||||||
tracked_comps.create_entity_package(entity),
|
tracked_comps.create_entity_package(entity),
|
||||||
@ -179,3 +211,69 @@ impl<'a> System<'a> for Sys {
|
|||||||
timer.end();
|
timer.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize region subscription
|
||||||
|
pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
||||||
|
if let (Some(client_pos), Some(client_vd), Some(client)) = (
|
||||||
|
world.read_storage::<Pos>().get(entity),
|
||||||
|
world
|
||||||
|
.read_storage::<Player>()
|
||||||
|
.get(entity)
|
||||||
|
.map(|pl| pl.view_distance)
|
||||||
|
.and_then(|v| v),
|
||||||
|
world.write_storage::<Client>().get_mut(entity),
|
||||||
|
) {
|
||||||
|
let fuzzy_chunk = (Vec2::<f32>::from(client_pos.0))
|
||||||
|
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||||
|
let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
||||||
|
let regions = common::region::regions_in_vd(
|
||||||
|
client_pos.0,
|
||||||
|
(client_vd as f32 * chunk_size) as f32
|
||||||
|
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let region_map = world.read_resource::<RegionMap>();
|
||||||
|
let tracked_comps = TrackedComps::fetch(&world.res);
|
||||||
|
for key in ®ions {
|
||||||
|
if let Some(region) = region_map.get(*key) {
|
||||||
|
for (uid, pos, vel, ori, character_state, _, entity) in (
|
||||||
|
&tracked_comps.uid,
|
||||||
|
&world.read_storage::<Pos>(), // We assume all these entities have a position
|
||||||
|
world.read_storage::<Vel>().maybe(),
|
||||||
|
world.read_storage::<Ori>().maybe(),
|
||||||
|
world.read_storage::<CharacterState>().maybe(),
|
||||||
|
region.entities(),
|
||||||
|
&world.entities(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
{
|
||||||
|
// Send message to create entity and tracked components
|
||||||
|
client.notify(ServerMsg::CreateEntity(
|
||||||
|
tracked_comps.create_entity_package(entity),
|
||||||
|
));
|
||||||
|
// Send message to create physics components
|
||||||
|
super::entity_sync::send_initial_unsynced_components(
|
||||||
|
client,
|
||||||
|
uid,
|
||||||
|
pos,
|
||||||
|
vel,
|
||||||
|
ori,
|
||||||
|
character_state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = world.write_storage().insert(
|
||||||
|
entity,
|
||||||
|
RegionSubscription {
|
||||||
|
fuzzy_chunk,
|
||||||
|
regions,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
error!("Failed to insert region subscription component: {:?}", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("Failed to initialize region subcription. Couldn't retrieve all the neccesary components on the provided entity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -112,6 +112,7 @@ widget_ids! {
|
|||||||
velocity,
|
velocity,
|
||||||
loaded_distance,
|
loaded_distance,
|
||||||
time,
|
time,
|
||||||
|
entity_count,
|
||||||
|
|
||||||
// Game Version
|
// Game Version
|
||||||
version,
|
version,
|
||||||
@ -872,11 +873,19 @@ impl Hud {
|
|||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.time, ui_widgets);
|
.set(self.ids.time, ui_widgets);
|
||||||
|
// Number of entities
|
||||||
|
let entity_count = client.state().ecs().entities().join().count();
|
||||||
|
Text::new(&format!("Entity count: {}", entity_count))
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.down_from(self.ids.time, 5.0)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.font_size(14)
|
||||||
|
.set(self.ids.entity_count, ui_widgets);
|
||||||
|
|
||||||
// Help Window
|
// Help Window
|
||||||
Text::new("Press 'F1' to show Keybindings")
|
Text::new("Press 'F1' to show Keybindings")
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.down_from(self.ids.time, 5.0)
|
.down_from(self.ids.entity_count, 5.0)
|
||||||
.font_id(self.fonts.cyri)
|
.font_id(self.fonts.cyri)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.help_info, ui_widgets);
|
.set(self.ids.help_info, ui_widgets);
|
||||||
|
@ -324,7 +324,7 @@ impl WorldSim {
|
|||||||
let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5;
|
let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5;
|
||||||
|
|
||||||
let erosion_pow = 2.0;
|
let erosion_pow = 2.0;
|
||||||
let n_steps = 100;
|
let n_steps = 7;
|
||||||
let erosion_factor = |x: f64| logistic_cdf(erosion_pow * logit(x));
|
let erosion_factor = |x: f64| logistic_cdf(erosion_pow * logit(x));
|
||||||
let alt = do_erosion(
|
let alt = do_erosion(
|
||||||
0.0,
|
0.0,
|
||||||
|
Loading…
Reference in New Issue
Block a user