use common::{ comp::{ Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Combo, Energy, Gravity, Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori, Player, Poise, Pos, Scale, Shockwave, Stats, Sticky, Vel, }, uid::Uid, }; use common_ecs::{Job, Origin, Phase, System}; use common_net::{ msg::EcsCompPacket, sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, UpdateTracker, WorldSyncExt}, }; use hashbrown::HashMap; use specs::{ shred::ResourceId, Entity as EcsEntity, Join, ReadExpect, ReadStorage, SystemData, World, WriteExpect, }; use vek::*; /// Always watching /// This system will monitor specific components for insertion, removal, and /// modification #[derive(Default)] pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = (TrackedComps<'a>, WriteTrackers<'a>); const NAME: &'static str = "sentinel"; const ORIGIN: Origin = Origin::Server; const PHASE: Phase = Phase::Create; fn run(_job: &mut Job, (comps, mut trackers): Self::SystemData) { record_changes(&comps, &mut trackers); } } // Probably more difficult than it needs to be :p #[derive(SystemData)] pub struct TrackedComps<'a> { pub uid: ReadStorage<'a, Uid>, pub body: ReadStorage<'a, Body>, pub player: ReadStorage<'a, Player>, pub stats: ReadStorage<'a, Stats>, pub buffs: ReadStorage<'a, Buffs>, pub auras: ReadStorage<'a, Auras>, pub energy: ReadStorage<'a, Energy>, pub combo: ReadStorage<'a, Combo>, pub health: ReadStorage<'a, Health>, pub poise: ReadStorage<'a, Poise>, 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 group: ReadStorage<'a, Group>, pub mass: ReadStorage<'a, Mass>, pub collider: ReadStorage<'a, Collider>, pub sticky: ReadStorage<'a, Sticky>, pub gravity: ReadStorage<'a, Gravity>, pub inventory: ReadStorage<'a, Inventory>, pub character_state: ReadStorage<'a, CharacterState>, pub shockwave: ReadStorage<'a, Shockwave>, pub beam_segment: ReadStorage<'a, BeamSegment>, } impl<'a> TrackedComps<'a> { pub fn create_entity_package( &self, entity: EcsEntity, pos: Option, vel: Option, ori: Option, ) -> EntityPackage { let uid = self .uid .get(entity) .copied() .expect("No uid to create an entity package") .0; let mut comps = Vec::new(); self.body.get(entity).copied().map(|c| comps.push(c.into())); self.player .get(entity) .cloned() .map(|c| comps.push(c.into())); self.stats .get(entity) .cloned() .map(|c| comps.push(c.into())); self.buffs .get(entity) .cloned() .map(|c| comps.push(c.into())); self.auras .get(entity) .cloned() .map(|c| comps.push(c.into())); self.energy .get(entity) .cloned() .map(|c| comps.push(c.into())); self.combo .get(entity) .cloned() .map(|c| comps.push(c.into())); self.health .get(entity) .cloned() .map(|c| comps.push(c.into())); self.poise .get(entity) .cloned() .map(|c| comps.push(c.into())); self.can_build .get(entity) .cloned() .map(|c| comps.push(c.into())); self.light_emitter .get(entity) .copied() .map(|c| comps.push(c.into())); self.item.get(entity).cloned().map(|c| comps.push(c.into())); self.scale .get(entity) .copied() .map(|c| comps.push(c.into())); self.mounting .get(entity) .cloned() .map(|c| comps.push(c.into())); self.mount_state .get(entity) .cloned() .map(|c| comps.push(c.into())); self.group .get(entity) .cloned() .map(|c| comps.push(c.into())); self.mass.get(entity).copied().map(|c| comps.push(c.into())); self.collider .get(entity) .cloned() .map(|c| comps.push(c.into())); self.sticky .get(entity) .copied() .map(|c| comps.push(c.into())); self.gravity .get(entity) .copied() .map(|c| comps.push(c.into())); self.inventory .get(entity) .cloned() .map(|c| comps.push(c.into())); self.character_state .get(entity) .cloned() .map(|c| comps.push(c.into())); self.shockwave .get(entity) .cloned() .map(|c| comps.push(c.into())); self.beam_segment .get(entity) .cloned() .map(|c| comps.push(c.into())); // Add untracked comps pos.map(|c| comps.push(c.into())); vel.map(|c| comps.push(c.into())); ori.map(|c| comps.push(c.into())); EntityPackage { uid, comps } } } #[derive(SystemData)] pub struct ReadTrackers<'a> { pub uid: ReadExpect<'a, UpdateTracker>, pub body: ReadExpect<'a, UpdateTracker>, pub player: ReadExpect<'a, UpdateTracker>, pub stats: ReadExpect<'a, UpdateTracker>, pub buffs: ReadExpect<'a, UpdateTracker>, pub auras: ReadExpect<'a, UpdateTracker>, pub energy: ReadExpect<'a, UpdateTracker>, pub combo: ReadExpect<'a, UpdateTracker>, pub health: ReadExpect<'a, UpdateTracker>, pub poise: ReadExpect<'a, UpdateTracker>, pub can_build: ReadExpect<'a, UpdateTracker>, pub light_emitter: ReadExpect<'a, UpdateTracker>, pub inventory: ReadExpect<'a, UpdateTracker>, pub item: ReadExpect<'a, UpdateTracker>, pub scale: ReadExpect<'a, UpdateTracker>, pub mounting: ReadExpect<'a, UpdateTracker>, pub mount_state: ReadExpect<'a, UpdateTracker>, pub group: ReadExpect<'a, UpdateTracker>, pub mass: ReadExpect<'a, UpdateTracker>, pub collider: ReadExpect<'a, UpdateTracker>, pub sticky: ReadExpect<'a, UpdateTracker>, pub gravity: ReadExpect<'a, UpdateTracker>, pub character_state: ReadExpect<'a, UpdateTracker>, pub shockwave: ReadExpect<'a, UpdateTracker>, pub beam_segment: ReadExpect<'a, UpdateTracker>, } impl<'a> ReadTrackers<'a> { pub fn create_sync_packages( &self, comps: &TrackedComps, filter: impl Join + Copy, deleted_entities: Vec, ) -> (EntitySyncPackage, CompSyncPackage) { let entity_sync_package = EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities); let comp_sync_package = CompSyncPackage::new() .with_component(&comps.uid, &*self.body, &comps.body, filter) .with_component(&comps.uid, &*self.player, &comps.player, filter) .with_component(&comps.uid, &*self.stats, &comps.stats, filter) .with_component(&comps.uid, &*self.buffs, &comps.buffs, filter) .with_component(&comps.uid, &*self.auras, &comps.auras, filter) .with_component(&comps.uid, &*self.energy, &comps.energy, filter) .with_component(&comps.uid, &*self.combo, &comps.combo, filter) .with_component(&comps.uid, &*self.health, &comps.health, filter) .with_component(&comps.uid, &*self.poise, &comps.poise, filter) .with_component(&comps.uid, &*self.can_build, &comps.can_build, filter) .with_component( &comps.uid, &*self.light_emitter, &comps.light_emitter, filter, ) .with_component(&comps.uid, &*self.item, &comps.item, filter) .with_component(&comps.uid, &*self.scale, &comps.scale, filter) .with_component(&comps.uid, &*self.mounting, &comps.mounting, filter) .with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter) .with_component(&comps.uid, &*self.group, &comps.group, filter) .with_component(&comps.uid, &*self.mass, &comps.mass, filter) .with_component(&comps.uid, &*self.collider, &comps.collider, filter) .with_component(&comps.uid, &*self.sticky, &comps.sticky, filter) .with_component(&comps.uid, &*self.gravity, &comps.gravity, filter) .with_component(&comps.uid, &*self.inventory, &comps.inventory, filter) .with_component( &comps.uid, &*self.character_state, &comps.character_state, filter, ) .with_component(&comps.uid, &*self.shockwave, &comps.shockwave, filter) .with_component(&comps.uid, &*self.beam_segment, &comps.beam_segment, filter); (entity_sync_package, comp_sync_package) } } #[derive(SystemData)] pub struct WriteTrackers<'a> { uid: WriteExpect<'a, UpdateTracker>, body: WriteExpect<'a, UpdateTracker>, player: WriteExpect<'a, UpdateTracker>, stats: WriteExpect<'a, UpdateTracker>, buffs: WriteExpect<'a, UpdateTracker>, auras: WriteExpect<'a, UpdateTracker>, energy: WriteExpect<'a, UpdateTracker>, combo: WriteExpect<'a, UpdateTracker>, health: WriteExpect<'a, UpdateTracker>, poise: WriteExpect<'a, UpdateTracker>, can_build: WriteExpect<'a, UpdateTracker>, light_emitter: WriteExpect<'a, UpdateTracker>, item: WriteExpect<'a, UpdateTracker>, scale: WriteExpect<'a, UpdateTracker>, mounting: WriteExpect<'a, UpdateTracker>, mount_state: WriteExpect<'a, UpdateTracker>, group: WriteExpect<'a, UpdateTracker>, mass: WriteExpect<'a, UpdateTracker>, collider: WriteExpect<'a, UpdateTracker>, sticky: WriteExpect<'a, UpdateTracker>, gravity: WriteExpect<'a, UpdateTracker>, inventory: WriteExpect<'a, UpdateTracker>, character_state: WriteExpect<'a, UpdateTracker>, shockwave: WriteExpect<'a, UpdateTracker>, beam: WriteExpect<'a, UpdateTracker>, } fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { // Update trackers trackers.uid.record_changes(&comps.uid); trackers.body.record_changes(&comps.body); trackers.player.record_changes(&comps.player); trackers.stats.record_changes(&comps.stats); trackers.buffs.record_changes(&comps.buffs); trackers.auras.record_changes(&comps.auras); trackers.energy.record_changes(&comps.energy); trackers.combo.record_changes(&comps.combo); trackers.health.record_changes(&comps.health); trackers.poise.record_changes(&comps.poise); trackers.can_build.record_changes(&comps.can_build); trackers.light_emitter.record_changes(&comps.light_emitter); trackers.item.record_changes(&comps.item); trackers.scale.record_changes(&comps.scale); trackers.mounting.record_changes(&comps.mounting); trackers.mount_state.record_changes(&comps.mount_state); trackers.group.record_changes(&comps.group); trackers.mass.record_changes(&comps.mass); trackers.collider.record_changes(&comps.collider); trackers.sticky.record_changes(&comps.sticky); trackers.gravity.record_changes(&comps.gravity); trackers.inventory.record_changes(&comps.inventory); trackers .character_state .record_changes(&comps.character_state); trackers.shockwave.record_changes(&comps.shockwave); trackers.beam.record_changes(&comps.beam_segment); // Debug how many updates are being sent /* macro_rules! log_counts { ($comp:ident, $name:expr) => { // Note: if this will be used in actual server it would be more efficient to // count during record_changes let tracker = &trackers.$comp; let inserted = tracker.inserted().into_iter().count(); let modified = tracker.modified().into_iter().count(); let removed = tracker.removed().into_iter().count(); tracing::warn!("{:6} insertions detected for {}", inserted, $name); tracing::warn!("{:6} modifications detected for {}", modified, $name); tracing::warn!("{:6} deletions detected for {}", removed, $name); }; }; log_counts!(uid, "Uids"); log_counts!(body, "Bodies"); log_counts!(buffs, "Buffs"); log_counts!(auras, "Auras"); log_counts!(player, "Players"); log_counts!(stats, "Stats"); log_counts!(energy, "Energies"); log_counts!(combo, "Combos"); log_vounts!(health, "Healths"); log_vounts!(poise, "Poises"); log_counts!(light_emitter, "Light emitters"); log_counts!(item, "Items"); log_counts!(scale, "Scales"); log_counts!(mounting, "Mountings"); log_counts!(mount_state, "Mount States"); log_counts!(mass, "Masses"); log_counts!(collider, "Colliders"); log_counts!(sticky, "Stickies"); log_counts!(gravity, "Gravitys"); log_counts!(loadout, "Loadouts"); log_counts!(character_state, "Character States"); log_counts!(shockwave, "Shockwaves"); log_counts!(beam, "Beams"); */ } pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); } /// Deleted entities grouped by region pub struct DeletedEntities { map: HashMap, Vec>, } 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) { self.map .entry(region_key) .or_insert(Vec::new()) .push(uid.into()); } pub fn take_deleted_in_region(&mut self, key: Vec2) -> Option> { self.map.remove(&key) } pub fn get_deleted_in_region(&mut self, key: Vec2) -> Option<&Vec> { self.map.get(&key) } pub fn take_remaining_deleted(&mut self) -> Vec<(Vec2, Vec)> { // TODO: don't allocate self.map.drain().collect() } }