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
|
||||
debug-assertions = true
|
||||
panic = "abort"
|
||||
debug = false
|
||||
# debug = false
|
||||
codegen-units = 8
|
||||
lto = false
|
||||
incremental = true
|
||||
|
@ -562,10 +562,15 @@ impl Client {
|
||||
self.state.ecs_mut().apply_entity_package(entity_package)
|
||||
}
|
||||
ServerMsg::DeleteEntity(entity) => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
if entity != self.entity {
|
||||
let _ = self.state.ecs_mut().delete_entity(entity);
|
||||
}
|
||||
if self
|
||||
.state
|
||||
.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 } => {
|
||||
|
@ -25,9 +25,6 @@ impl sync::ResPacket for EcsResPacket {
|
||||
sum_type! {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum EcsCompPacket {
|
||||
Pos(comp::Pos),
|
||||
Vel(comp::Vel),
|
||||
Ori(comp::Ori),
|
||||
Body(comp::Body),
|
||||
Player(comp::Player),
|
||||
CanBuild(comp::CanBuild),
|
||||
@ -48,9 +45,6 @@ sum_type! {
|
||||
sum_type! {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum EcsCompPhantom {
|
||||
Pos(PhantomData<comp::Pos>),
|
||||
Vel(PhantomData<comp::Vel>),
|
||||
Ori(PhantomData<comp::Ori>),
|
||||
Body(PhantomData<comp::Body>),
|
||||
Player(PhantomData<comp::Player>),
|
||||
CanBuild(PhantomData<comp::CanBuild>),
|
||||
@ -70,9 +64,6 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
type Phantom = EcsCompPhantom;
|
||||
fn apply_insert(self, entity: specs::Entity, world: &specs::World) {
|
||||
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::Player(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) {
|
||||
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::Player(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) {
|
||||
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::Player(_) => sync::handle_remove::<comp::Player>(entity, world),
|
||||
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
|
||||
|
@ -104,6 +104,16 @@ impl RegionMap {
|
||||
// TODO special case large entities
|
||||
pub fn tick(&mut self, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>, entities: Entities) {
|
||||
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
|
||||
for (pos, id) in (&pos, &entities, !&self.tracked_entities)
|
||||
.join()
|
||||
@ -118,14 +128,6 @@ impl RegionMap {
|
||||
let mut regions_to_remove = Vec::new();
|
||||
|
||||
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 (
|
||||
pos.maybe(),
|
||||
vel.maybe(),
|
||||
@ -215,6 +217,47 @@ impl RegionMap {
|
||||
pub fn key_pos(key: Vec2<i32>) -> Vec2<i32> {
|
||||
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> {
|
||||
self.regions.get_full(&key).map(|(i, _, _)| i)
|
||||
}
|
||||
|
@ -148,10 +148,10 @@ impl State {
|
||||
ecs.register::<comp::Waypoint>();
|
||||
|
||||
// Register synced resources used by the ECS.
|
||||
ecs.add_resource(Time(0.0));
|
||||
ecs.add_resource(TimeOfDay(0.0));
|
||||
|
||||
// Register unsynced resources used by the ECS.
|
||||
ecs.add_resource(Time(0.0));
|
||||
ecs.add_resource(DeltaTime(0.0));
|
||||
ecs.add_resource(TerrainGrid::new().unwrap());
|
||||
ecs.add_resource(BlockChange::default());
|
||||
|
@ -2,6 +2,7 @@ use super::{
|
||||
track::{Tracker, UpdateTracker},
|
||||
uid::Uid,
|
||||
};
|
||||
use log::error;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use specs::{shred::Resource, Component, Entity, Join, ReadStorage, World};
|
||||
use std::{
|
||||
@ -24,7 +25,9 @@ pub trait ResPacket: Clone + Debug + Send + 'static {
|
||||
|
||||
/// Useful for implementing CompPacket trait
|
||||
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
|
||||
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>,
|
||||
uid_tracker: &UpdateTracker<Uid>,
|
||||
filter: impl Join + Copy,
|
||||
deleted_entities: Vec<u64>,
|
||||
) -> Self {
|
||||
// Add created and deleted entities
|
||||
let created_entities = (uids, filter, uid_tracker.inserted())
|
||||
@ -110,10 +114,15 @@ impl<P: CompPacket> SyncPackage<P> {
|
||||
.collect();
|
||||
// TODO: handle modified uid?
|
||||
//created_entities.append(&mut (uids, filter, uid_tracker.inserted()).join().map(|(uid, _, _)| uid).collect());
|
||||
let deleted_entities = (uids, filter, uid_tracker.removed())
|
||||
.join()
|
||||
.map(|(uid, _, _)| (*uid).into())
|
||||
.collect();
|
||||
// let deleted_entities = (uids.maybe(), filter, uid_tracker.removed())
|
||||
// .join()
|
||||
// Why doesn't this panic??
|
||||
// .map(|(uid, _, _)| Into::<u64>::into(*uid.unwrap()))
|
||||
// .collect::<Vec<_>>();
|
||||
//let len = deleted_entities.len();
|
||||
//if len > 0 {
|
||||
// println!("deleted {} in sync message", len);
|
||||
// }
|
||||
|
||||
Self {
|
||||
comp_updates: Vec::new(),
|
||||
@ -124,7 +133,6 @@ impl<P: CompPacket> SyncPackage<P> {
|
||||
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
|
||||
mut self,
|
||||
uids: &ReadStorage<'a, Uid>,
|
||||
uid_tracker: &UpdateTracker<Uid>,
|
||||
tracker: &impl Tracker<C, P>,
|
||||
storage: &ReadStorage<'a, C>,
|
||||
filter: impl Join + Copy,
|
||||
@ -136,13 +144,7 @@ impl<P: CompPacket> SyncPackage<P> {
|
||||
P::Phantom: TryInto<PhantomData<C>>,
|
||||
C::Storage: specs::storage::Tracked,
|
||||
{
|
||||
tracker.get_updates_for(
|
||||
uids,
|
||||
storage,
|
||||
// Don't include updates for deleted entities
|
||||
(filter, &!uid_tracker.removed()),
|
||||
&mut self.comp_updates,
|
||||
);
|
||||
tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use super::{
|
||||
track::UpdateTracker,
|
||||
uid::{Uid, UidAllocator},
|
||||
};
|
||||
use log::error;
|
||||
use specs::{
|
||||
saveload::{MarkedBuilder, MarkerAllocator},
|
||||
world::Builder,
|
||||
@ -20,6 +21,7 @@ pub trait WorldSyncExt {
|
||||
where
|
||||
C::Storage: Default + specs::storage::Tracked;
|
||||
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 entity_from_uid(&self, uid: u64) -> Option<specs::Entity>;
|
||||
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) {
|
||||
self.register_synced::<Uid>();
|
||||
|
||||
// TODO: Consider only having allocator server side for now
|
||||
self.add_resource(UidAllocator::new());
|
||||
}
|
||||
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||
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,
|
||||
{
|
||||
self.register::<C>();
|
||||
@ -50,26 +49,12 @@ impl WorldSyncExt for specs::World {
|
||||
}
|
||||
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||
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,
|
||||
{
|
||||
let tracker = UpdateTracker::<C>::new(self);
|
||||
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 {
|
||||
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>(
|
||||
&mut self,
|
||||
state_package: StatePackage<P, R>,
|
||||
@ -114,7 +109,7 @@ impl WorldSyncExt for specs::World {
|
||||
}
|
||||
|
||||
// Initialize entities
|
||||
//specs_world.maintain();
|
||||
//self.maintain();
|
||||
}
|
||||
|
||||
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
|
||||
for entity_uid in deleted_entities {
|
||||
let entity = self
|
||||
.read_resource::<UidAllocator>()
|
||||
.retrieve_entity_internal(entity_uid);
|
||||
if let Some(entity) = entity {
|
||||
let _ = self.delete_entity(entity);
|
||||
}
|
||||
self.delete_entity_and_clear_from_uid_allocator(entity_uid);
|
||||
}
|
||||
}
|
||||
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>()
|
||||
.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) => {
|
||||
// We don't care about modification if the component was just added or was
|
||||
// removed
|
||||
// Could potentially remove since this should theoretically never occur...
|
||||
if !self.removed.contains(*id) && !self.inserted.contains(*id) {
|
||||
self.modified.add(*id);
|
||||
}
|
||||
|
@ -56,6 +56,10 @@ impl UidAllocator {
|
||||
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 {
|
||||
@ -69,14 +73,14 @@ impl MarkerAllocator<Uid> for UidAllocator {
|
||||
let id = id.unwrap_or_else(|| {
|
||||
let id = self.index;
|
||||
self.index += 1;
|
||||
self.mapping.insert(id, entity);
|
||||
id
|
||||
});
|
||||
self.mapping.insert(id, entity);
|
||||
Uid(id)
|
||||
}
|
||||
|
||||
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>) {
|
||||
|
@ -21,6 +21,7 @@ use vek::*;
|
||||
use world::util::Sampler;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::error;
|
||||
use scan_fmt::{scan_fmt, scan_fmt_some};
|
||||
|
||||
/// 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();
|
||||
|
||||
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(
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
chunk_generator::ChunkGenerator,
|
||||
client::{Client, RegionSubscription},
|
||||
cmd::CHAT_COMMANDS,
|
||||
sys::sentinel::{TrackedComps, TrackedResources},
|
||||
sys::sentinel::{DeletedEntities, TrackedComps, TrackedResources},
|
||||
};
|
||||
use common::{
|
||||
assets, comp,
|
||||
@ -32,7 +32,7 @@ use common::{
|
||||
terrain::{block::Block, TerrainChunkSize, TerrainGrid},
|
||||
vol::{ReadVol, RectVolSize, Vox},
|
||||
};
|
||||
use log::debug;
|
||||
use log::{debug, error};
|
||||
use metrics::ServerMetrics;
|
||||
use rand::Rng;
|
||||
use specs::{
|
||||
@ -166,6 +166,8 @@ impl Server {
|
||||
// Register trackers
|
||||
sys::sentinel::register_trackers(&mut state.ecs_mut());
|
||||
|
||||
state.ecs_mut().add_resource(DeletedEntities::default());
|
||||
|
||||
let this = Self {
|
||||
state,
|
||||
world: Arc::new(world),
|
||||
@ -309,8 +311,6 @@ impl Server {
|
||||
|
||||
let server_settings = &self.server_settings;
|
||||
|
||||
let mut todo_remove = None;
|
||||
|
||||
match event {
|
||||
ServerEvent::Explosion { pos, radius } => {
|
||||
const RAYS: usize = 500;
|
||||
@ -380,19 +380,20 @@ impl Server {
|
||||
}
|
||||
|
||||
ServerEvent::Destroy { entity, cause } => {
|
||||
let ecs = state.ecs();
|
||||
// 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 {
|
||||
ecs.entity_from_uid(by.into()).and_then(|attacker| {
|
||||
ecs.read_storage::<comp::Player>().get(attacker).map(
|
||||
|attacker_alias| {
|
||||
state.ecs().entity_from_uid(by.into()).and_then(|attacker| {
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<comp::Player>()
|
||||
.get(attacker)
|
||||
.map(|attacker_alias| {
|
||||
format!(
|
||||
"{} was killed by {}",
|
||||
&player.alias, &attacker_alias.alias
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -402,12 +403,12 @@ impl Server {
|
||||
state.notify_registered_clients(ServerMsg::kill(msg));
|
||||
}
|
||||
|
||||
{
|
||||
// Give EXP to the killer if entity had stats
|
||||
let mut stats = ecs.write_storage::<comp::Stats>();
|
||||
|
||||
let mut stats = state.ecs().write_storage::<comp::Stats>();
|
||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||
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) {
|
||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||
attacker_stats
|
||||
@ -417,13 +418,29 @@ impl Server {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(client) = ecs.write_storage::<Client>().get_mut(entity) {
|
||||
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
|
||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
||||
// This sucks
|
||||
let mut remove = false;
|
||||
|
||||
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);
|
||||
} 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 {
|
||||
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);
|
||||
@ -728,7 +747,7 @@ impl Server {
|
||||
main,
|
||||
&server_settings,
|
||||
);
|
||||
Self::initialize_region_subscription(state, entity);
|
||||
sys::subscription::initialize_region_subscription(state.ecs(), entity);
|
||||
}
|
||||
|
||||
ServerEvent::CreateNpc {
|
||||
@ -746,8 +765,8 @@ impl Server {
|
||||
}
|
||||
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
if let Err(err) = state.ecs_mut().delete_entity(entity) {
|
||||
debug!("Failed to delete disconnected client: {:?}", err);
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete disconnected client: {:?}", err);
|
||||
}
|
||||
|
||||
frontend_events.push(Event::ClientDisconnected { entity });
|
||||
@ -761,11 +780,6 @@ impl Server {
|
||||
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.
|
||||
@ -825,7 +839,7 @@ impl Server {
|
||||
frontend_events.append(&mut self.handle_new_connections()?);
|
||||
|
||||
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);
|
||||
|
||||
let before_handle_events = Instant::now();
|
||||
@ -856,7 +870,9 @@ impl Server {
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
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();
|
||||
@ -1004,83 +1020,6 @@ impl Server {
|
||||
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) {
|
||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.notify(msg)
|
||||
@ -1142,6 +1081,10 @@ trait StateExt {
|
||||
stats: comp::Stats,
|
||||
body: comp::Body,
|
||||
) -> EcsEntityBuilder;
|
||||
fn delete_entity_recorded(
|
||||
&mut self,
|
||||
entity: EcsEntity,
|
||||
) -> Result<(), specs::error::WrongGeneration>;
|
||||
}
|
||||
|
||||
impl StateExt for State {
|
||||
@ -1202,4 +1145,28 @@ impl StateExt for State {
|
||||
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::{
|
||||
sentinel::{ReadTrackers, TrackedComps, TrackedResources},
|
||||
sentinel::{DeletedEntities, ReadTrackers, TrackedComps, TrackedResources},
|
||||
SysTimer,
|
||||
};
|
||||
use crate::{
|
||||
@ -38,6 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, ForceUpdate>,
|
||||
WriteStorage<'a, InventoryUpdate>,
|
||||
Write<'a, DeletedEntities>,
|
||||
TrackedComps<'a>,
|
||||
ReadTrackers<'a>,
|
||||
TrackedResources<'a>,
|
||||
@ -64,8 +65,9 @@ impl<'a> System<'a> for Sys {
|
||||
mut clients,
|
||||
mut force_updates,
|
||||
mut inventory_updates,
|
||||
mut deleted_entities,
|
||||
tracked_comps,
|
||||
read_trackers,
|
||||
trackers,
|
||||
tracked_resources,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
@ -86,6 +88,8 @@ impl<'a> System<'a> for Sys {
|
||||
// Sync physics
|
||||
// via iterating through regions
|
||||
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)
|
||||
.join()
|
||||
.filter_map(|(client, entity, subscription, pos)| {
|
||||
@ -100,6 +104,10 @@ impl<'a> System<'a> for Sys {
|
||||
for event in region.events() {
|
||||
match event {
|
||||
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);
|
||||
if let Some((uid, pos, vel, ori, character_state)) =
|
||||
uids.get(entity).and_then(|uid| {
|
||||
@ -156,8 +164,15 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Sync tracked components
|
||||
// Get deleted entities in this region from DeletedEntities
|
||||
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 {
|
||||
client.notify(sync_msg.clone());
|
||||
@ -169,9 +184,9 @@ impl<'a> System<'a> for Sys {
|
||||
force_update: Option<&ForceUpdate>,
|
||||
throttle: bool| {
|
||||
for (client, _, client_entity, client_pos) in &mut subscribers {
|
||||
let update = if client_entity == &entity && force_update.is_none() {
|
||||
// Don't send client physics update about itself
|
||||
false
|
||||
let update = if client_entity == &entity {
|
||||
// Don't send client physics updates about itself unless force update is set
|
||||
force_update.is_some()
|
||||
} else if !throttle {
|
||||
// Update rate not thottled by distance
|
||||
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?
|
||||
|
||||
// Sync inventories
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::SysTimer;
|
||||
use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
|
||||
use common::{
|
||||
comp::{Admin, Body, CanBuild, Controller, Ori, Player, Pos, Vel},
|
||||
comp::{Admin, Body, CanBuild, Controller, ForceUpdate, Ori, Player, Pos, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{validate_chat_msg, ChatMsgValidationError, MAX_BYTES_CHAT_MSG},
|
||||
msg::{ClientMsg, ClientState, RequestStateError, ServerMsg},
|
||||
@ -25,6 +25,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, CanBuild>,
|
||||
ReadStorage<'a, Admin>,
|
||||
ReadStorage<'a, ForceUpdate>,
|
||||
WriteExpect<'a, AuthProvider>,
|
||||
Write<'a, BlockChange>,
|
||||
WriteStorage<'a, Pos>,
|
||||
@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys {
|
||||
bodies,
|
||||
can_build,
|
||||
admins,
|
||||
force_updates,
|
||||
mut accounts,
|
||||
mut block_changes,
|
||||
mut positions,
|
||||
@ -218,10 +220,12 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
||||
ClientState::Character => {
|
||||
if force_updates.get(entity).is_none() {
|
||||
let _ = positions.insert(entity, pos);
|
||||
let _ = velocities.insert(entity, vel);
|
||||
let _ = orientations.insert(entity, ori);
|
||||
}
|
||||
}
|
||||
// Only characters can send positions.
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
},
|
||||
|
@ -5,17 +5,19 @@ use common::{
|
||||
Projectile, Scale, Stats, Sticky,
|
||||
},
|
||||
msg::{EcsCompPacket, EcsResPacket},
|
||||
state::{Time, TimeOfDay},
|
||||
state::TimeOfDay,
|
||||
sync::{
|
||||
CompPacket, EntityPackage, ResSyncPackage, StatePackage, SyncPackage, Uid, UpdateTracker,
|
||||
WorldSyncExt,
|
||||
},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use shred_derive::SystemData;
|
||||
use specs::{
|
||||
Entity as EcsEntity, Join, ReadExpect, ReadStorage, System, World, Write, WriteExpect,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
use vek::*;
|
||||
|
||||
/// Always watching
|
||||
/// 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
|
||||
#[derive(SystemData)]
|
||||
pub struct TrackedComps<'a> {
|
||||
uid: ReadStorage<'a, Uid>,
|
||||
body: ReadStorage<'a, Body>,
|
||||
player: ReadStorage<'a, Player>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
can_build: ReadStorage<'a, CanBuild>,
|
||||
light_emitter: ReadStorage<'a, LightEmitter>,
|
||||
item: ReadStorage<'a, Item>,
|
||||
scale: ReadStorage<'a, Scale>,
|
||||
mounting: ReadStorage<'a, Mounting>,
|
||||
mount_state: ReadStorage<'a, MountState>,
|
||||
mass: ReadStorage<'a, Mass>,
|
||||
sticky: ReadStorage<'a, Sticky>,
|
||||
gravity: ReadStorage<'a, Gravity>,
|
||||
projectile: ReadStorage<'a, Projectile>,
|
||||
pub uid: ReadStorage<'a, Uid>,
|
||||
pub body: ReadStorage<'a, Body>,
|
||||
pub player: ReadStorage<'a, Player>,
|
||||
pub stats: ReadStorage<'a, Stats>,
|
||||
pub can_build: ReadStorage<'a, CanBuild>,
|
||||
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||
pub item: ReadStorage<'a, Item>,
|
||||
pub scale: ReadStorage<'a, Scale>,
|
||||
pub mounting: ReadStorage<'a, Mounting>,
|
||||
pub mount_state: ReadStorage<'a, MountState>,
|
||||
pub mass: ReadStorage<'a, Mass>,
|
||||
pub sticky: ReadStorage<'a, Sticky>,
|
||||
pub gravity: ReadStorage<'a, Gravity>,
|
||||
pub projectile: ReadStorage<'a, Projectile>,
|
||||
}
|
||||
impl<'a> TrackedComps<'a> {
|
||||
pub fn create_entity_package(&self, entity: EcsEntity) -> EntityPackage<EcsCompPacket> {
|
||||
@ -121,115 +123,53 @@ impl<'a> TrackedComps<'a> {
|
||||
}
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadTrackers<'a> {
|
||||
uid: ReadExpect<'a, UpdateTracker<Uid>>,
|
||||
body: ReadExpect<'a, UpdateTracker<Body>>,
|
||||
player: ReadExpect<'a, UpdateTracker<Player>>,
|
||||
stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
||||
can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
|
||||
light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
|
||||
item: ReadExpect<'a, UpdateTracker<Item>>,
|
||||
scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
||||
mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
||||
mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||
mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||
sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
||||
gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
||||
projectile: ReadExpect<'a, UpdateTracker<Projectile>>,
|
||||
pub uid: ReadExpect<'a, UpdateTracker<Uid>>,
|
||||
pub body: ReadExpect<'a, UpdateTracker<Body>>,
|
||||
pub player: ReadExpect<'a, UpdateTracker<Player>>,
|
||||
pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
||||
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
|
||||
pub light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
|
||||
pub item: ReadExpect<'a, UpdateTracker<Item>>,
|
||||
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
||||
pub mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
||||
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
||||
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
||||
pub projectile: ReadExpect<'a, UpdateTracker<Projectile>>,
|
||||
}
|
||||
impl<'a> ReadTrackers<'a> {
|
||||
pub fn create_sync_package(
|
||||
&self,
|
||||
comps: &TrackedComps,
|
||||
filter: impl Join + Copy,
|
||||
deleted_entities: Vec<u64>,
|
||||
) -> 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(
|
||||
&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(),
|
||||
&comps.light_emitter,
|
||||
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(
|
||||
&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(),
|
||||
&comps.mount_state,
|
||||
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(
|
||||
&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(),
|
||||
&comps.projectile,
|
||||
filter,
|
||||
@ -292,19 +232,46 @@ pub fn register_trackers(world: &mut World) {
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct TrackedResources<'a> {
|
||||
time: ReadExpect<'a, Time>,
|
||||
time_of_day: ReadExpect<'a, TimeOfDay>,
|
||||
}
|
||||
impl<'a> TrackedResources<'a> {
|
||||
pub fn create_res_sync_package(&self) -> ResSyncPackage<EcsResPacket> {
|
||||
ResSyncPackage::new()
|
||||
.with_res(self.time.deref())
|
||||
.with_res(self.time_of_day.deref())
|
||||
ResSyncPackage::new().with_res(self.time_of_day.deref())
|
||||
}
|
||||
/// Create state package with resources included
|
||||
pub fn state_package<C: CompPacket>(&self) -> StatePackage<C, EcsResPacket> {
|
||||
StatePackage::new()
|
||||
.with_res(self.time.deref())
|
||||
.with_res(self.time_of_day.deref())
|
||||
StatePackage::new().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 common::{
|
||||
comp::{CharacterState, Ori, Player, Pos, Vel},
|
||||
@ -8,7 +11,10 @@ use common::{
|
||||
terrain::TerrainChunkSize,
|
||||
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::*;
|
||||
|
||||
/// This system will update region subscriptions based on client positions
|
||||
@ -26,6 +32,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Player>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, RegionSubscription>,
|
||||
Write<'a, DeletedEntities>,
|
||||
TrackedComps<'a>,
|
||||
);
|
||||
|
||||
@ -43,6 +50,7 @@ impl<'a> System<'a> for Sys {
|
||||
players,
|
||||
mut clients,
|
||||
mut subscriptions,
|
||||
mut deleted_entities,
|
||||
tracked_comps,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
@ -68,8 +76,15 @@ impl<'a> System<'a> for Sys {
|
||||
&entities,
|
||||
)
|
||||
.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))
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||
// Only update regions when moving to a new chunk
|
||||
@ -87,7 +102,7 @@ impl<'a> System<'a> for Sys {
|
||||
.reduce_or()
|
||||
{
|
||||
// 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);
|
||||
// Use the largest side length as our chunk size
|
||||
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
|
||||
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);
|
||||
// Tell the client to delete the entities in that region if it exists in the RegionMap
|
||||
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() {
|
||||
match event {
|
||||
RegionEvent::Entered(_, _) => {} // These don't need to be processed because this region is being thrown out anyway
|
||||
RegionEvent::Left(id, maybe_key) => {
|
||||
// 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 !maybe_key
|
||||
.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))
|
||||
.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() {
|
||||
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(
|
||||
@ -141,8 +170,10 @@ impl<'a> System<'a> for Sys {
|
||||
(vd as f32 * chunk_size)
|
||||
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||
) {
|
||||
// Send client intial info about the entities in this region
|
||||
if subscription.regions.insert(key) {
|
||||
// Send client intial info about the entities in this region if it was not
|
||||
// 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) {
|
||||
for (uid, pos, vel, ori, character_state, _, entity) in (
|
||||
&uids,
|
||||
@ -156,6 +187,7 @@ impl<'a> System<'a> for Sys {
|
||||
.join()
|
||||
.filter(|(_, _, _, _, _, _, e)| *e != client_entity)
|
||||
{
|
||||
counter += 1;
|
||||
// Send message to create entity and tracked components
|
||||
client.notify(ServerMsg::CreateEntity(
|
||||
tracked_comps.create_entity_package(entity),
|
||||
@ -179,3 +211,69 @@ impl<'a> System<'a> for Sys {
|
||||
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,
|
||||
loaded_distance,
|
||||
time,
|
||||
entity_count,
|
||||
|
||||
// Game Version
|
||||
version,
|
||||
@ -872,11 +873,19 @@ impl Hud {
|
||||
.font_id(self.fonts.cyri)
|
||||
.font_size(14)
|
||||
.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
|
||||
Text::new("Press 'F1' to show Keybindings")
|
||||
.color(TEXT_COLOR)
|
||||
.down_from(self.ids.time, 5.0)
|
||||
.down_from(self.ids.entity_count, 5.0)
|
||||
.font_id(self.fonts.cyri)
|
||||
.font_size(14)
|
||||
.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 erosion_pow = 2.0;
|
||||
let n_steps = 100;
|
||||
let n_steps = 7;
|
||||
let erosion_factor = |x: f64| logistic_cdf(erosion_pow * logit(x));
|
||||
let alt = do_erosion(
|
||||
0.0,
|
||||
|
Loading…
Reference in New Issue
Block a user