veloren_server/sys/
terrain_sync.rs1#[cfg(not(feature = "worldgen"))]
2use crate::test_world::World;
3use crate::{Settings, chunk_serialize::ChunkSendEntry, client::Client};
4use common::{
5 comp::{Pos, Presence},
6 event::EventBus,
7};
8use common_ecs::{Job, Origin, Phase, System};
9use common_net::msg::{CompressedData, ServerGeneral};
10use common_state::TerrainChanges;
11use rayon::prelude::*;
12use specs::{Entities, Join, Read, ReadExpect, ReadStorage};
13use std::sync::Arc;
14#[cfg(feature = "worldgen")] use world::World;
15
16#[derive(Default)]
19pub struct Sys;
20impl<'a> System<'a> for Sys {
21 type SystemData = (
22 Entities<'a>,
23 ReadExpect<'a, Arc<World>>,
24 Read<'a, Settings>,
25 Read<'a, TerrainChanges>,
26 ReadExpect<'a, EventBus<ChunkSendEntry>>,
27 ReadStorage<'a, Pos>,
28 ReadStorage<'a, Presence>,
29 ReadStorage<'a, Client>,
30 );
31
32 const NAME: &'static str = "terrain_sync";
33 const ORIGIN: Origin = Origin::Server;
34 const PHASE: Phase = Phase::Create;
35
36 fn run(
37 _job: &mut Job<Self>,
38 (
39 entities,
40 world,
41 server_settings,
42 terrain_changes,
43 chunk_send_bus,
44 positions,
45 presences,
46 clients,
47 ): Self::SystemData,
48 ) {
49 let max_view_distance = server_settings.max_view_distance.unwrap_or(u32::MAX);
50 #[cfg(feature = "worldgen")]
51 let world_size = world.sim().get_size();
52 #[cfg(not(feature = "worldgen"))]
53 let world_size = world.map_size_lg().chunks().as_();
54 let (presences_position_entities, _) = super::terrain::prepare_player_presences(
55 world_size,
56 max_view_distance,
57 &entities,
58 &positions,
59 &presences,
60 &clients,
61 );
62 let real_max_view_distance =
63 super::terrain::convert_to_loaded_vd(u32::MAX, max_view_distance);
64
65 terrain_changes.modified_chunks.par_iter().for_each_init(
67 || chunk_send_bus.emitter(),
68 |chunk_send_emitter, &chunk_key| {
69 let min_chunk_x = chunk_key.x - real_max_view_distance;
77 let max_chunk_x = chunk_key.x + real_max_view_distance;
78 let start = presences_position_entities
79 .partition_point(|((pos, _), _)| i32::from(pos.x) < min_chunk_x);
80 let end = presences_position_entities
92 .partition_point(|((pos, _), _)| i32::from(pos.x) < max_chunk_x);
93 let interior = &presences_position_entities[start..end];
94 interior
95 .iter()
96 .filter(|((player_chunk_pos, player_vd_sqr), _)| {
97 super::terrain::chunk_in_vd(*player_chunk_pos, *player_vd_sqr, chunk_key)
98 })
99 .for_each(|(_, entity)| {
100 chunk_send_emitter.emit(ChunkSendEntry {
101 entity: *entity,
102 chunk_key,
103 });
104 });
105 },
106 );
107
108 if !terrain_changes.modified_blocks.is_empty() {
111 let mut lazy_msg = None;
112 for (_, client) in (&presences, &clients).join() {
113 if lazy_msg.is_none() {
114 lazy_msg = Some(client.prepare(ServerGeneral::TerrainBlockUpdates(
115 CompressedData::compress(&terrain_changes.modified_blocks, 1),
116 )));
117 }
118 lazy_msg.as_ref().map(|msg| client.send_prepared(msg));
119 }
120 }
121 }
122}