diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2025-02-22 21:45:26 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-22 21:45:26 -0600 |
| commit | e21e1b97bf9337e9f4747cd1b545b1b3a03e2ce7 (patch) | |
| tree | add6f8bfce40d0c07845d8aa4c9945a0b918444c /azalea-client/src/chunks.rs | |
| parent | f8130c3c92946d2293634ba4e252d6bc93026c3c (diff) | |
| download | azalea-drasl-e21e1b97bf9337e9f4747cd1b545b1b3a03e2ce7.tar.xz | |
Refactor azalea-client (#205)
* start organizing packet_handling more by moving packet handlers into their own functions
* finish writing all the handler functions for packets
* use macro for generating match statement for packet handler functions
* fix set_entity_data
* update config state to also use handler functions
* organize az-client file structure by moving things into plugins directory
* fix merge issues
Diffstat (limited to 'azalea-client/src/chunks.rs')
| -rw-r--r-- | azalea-client/src/chunks.rs | 225 |
1 files changed, 0 insertions, 225 deletions
diff --git a/azalea-client/src/chunks.rs b/azalea-client/src/chunks.rs deleted file mode 100644 index 67313757..00000000 --- a/azalea-client/src/chunks.rs +++ /dev/null @@ -1,225 +0,0 @@ -//! Used for Minecraft's chunk batching introduced in 23w31a (1.20.2). It's used -//! for making the server spread out how often it sends us chunk packets -//! depending on our receiving speed. - -use std::{ - io::Cursor, - ops::Deref, - time::{Duration, Instant}, -}; - -use azalea_core::position::ChunkPos; -use azalea_protocol::packets::game::{ - c_level_chunk_with_light::ClientboundLevelChunkWithLight, - s_chunk_batch_received::ServerboundChunkBatchReceived, -}; -use bevy_app::{App, Plugin, Update}; -use bevy_ecs::prelude::*; -use simdnbt::owned::BaseNbt; -use tracing::{error, trace}; - -use crate::{ - InstanceHolder, - interact::handle_block_interact_event, - inventory::InventorySet, - packet_handling::game::{SendPacketEvent, handle_send_packet_event}, - respawn::perform_respawn, -}; - -pub struct ChunkPlugin; -impl Plugin for ChunkPlugin { - fn build(&self, app: &mut App) { - app.add_systems( - Update, - ( - handle_chunk_batch_start_event, - handle_receive_chunk_events, - handle_chunk_batch_finished_event, - ) - .chain() - .before(handle_send_packet_event) - .before(InventorySet) - .before(handle_block_interact_event) - .before(perform_respawn), - ) - .add_event::<ReceiveChunkEvent>() - .add_event::<ChunkBatchStartEvent>() - .add_event::<ChunkBatchFinishedEvent>(); - } -} - -#[derive(Event)] -pub struct ReceiveChunkEvent { - pub entity: Entity, - pub packet: ClientboundLevelChunkWithLight, -} - -#[derive(Component, Clone, Debug)] -pub struct ChunkBatchInfo { - pub start_time: Instant, - pub aggregated_duration_per_chunk: Duration, - pub old_samples_weight: u32, -} - -#[derive(Event)] -pub struct ChunkBatchStartEvent { - pub entity: Entity, -} -#[derive(Event)] -pub struct ChunkBatchFinishedEvent { - pub entity: Entity, - pub batch_size: u32, -} - -pub fn handle_receive_chunk_events( - mut events: EventReader<ReceiveChunkEvent>, - mut query: Query<&mut InstanceHolder>, -) { - for event in events.read() { - let pos = ChunkPos::new(event.packet.x, event.packet.z); - - let local_player = query.get_mut(event.entity).unwrap(); - - let mut instance = local_player.instance.write(); - let mut partial_instance = local_player.partial_instance.write(); - - // OPTIMIZATION: if we already know about the chunk from the shared world (and - // not ourselves), then we don't need to parse it again. This is only used when - // we have a shared world, since we check that the chunk isn't currently owned - // by this client. - let shared_chunk = instance.chunks.get(&pos); - let this_client_has_chunk = partial_instance.chunks.limited_get(&pos).is_some(); - - if !this_client_has_chunk { - if let Some(shared_chunk) = shared_chunk { - trace!("Skipping parsing chunk {pos:?} because we already know about it"); - partial_instance - .chunks - .limited_set(&pos, Some(shared_chunk)); - continue; - } - } - - let heightmaps_nbt = &event.packet.chunk_data.heightmaps; - // necessary to make the unwrap_or work - let empty_nbt = BaseNbt::default(); - let heightmaps = heightmaps_nbt.unwrap_or(&empty_nbt).deref(); - - if let Err(e) = partial_instance.chunks.replace_with_packet_data( - &pos, - &mut Cursor::new(&event.packet.chunk_data.data), - heightmaps, - &mut instance.chunks, - ) { - error!( - "Couldn't set chunk data: {e}. World height: {}", - instance.chunks.height - ); - } - } -} - -impl ChunkBatchInfo { - pub fn batch_finished(&mut self, batch_size: u32) { - if batch_size == 0 { - return; - } - let batch_duration = self.start_time.elapsed(); - let duration_per_chunk = batch_duration / batch_size; - let clamped_duration = Duration::clamp( - duration_per_chunk, - self.aggregated_duration_per_chunk / 3, - self.aggregated_duration_per_chunk * 3, - ); - self.aggregated_duration_per_chunk = - ((self.aggregated_duration_per_chunk * self.old_samples_weight) + clamped_duration) - / (self.old_samples_weight + 1); - self.old_samples_weight = u32::min(49, self.old_samples_weight + 1); - } - - pub fn desired_chunks_per_tick(&self) -> f32 { - (7000000. / self.aggregated_duration_per_chunk.as_nanos() as f64) as f32 - } -} - -pub fn handle_chunk_batch_start_event( - mut query: Query<&mut ChunkBatchInfo>, - mut events: EventReader<ChunkBatchStartEvent>, -) { - for event in events.read() { - if let Ok(mut chunk_batch_info) = query.get_mut(event.entity) { - chunk_batch_info.start_time = Instant::now(); - } - } -} - -pub fn handle_chunk_batch_finished_event( - mut query: Query<&mut ChunkBatchInfo>, - mut events: EventReader<ChunkBatchFinishedEvent>, - mut send_packets: EventWriter<SendPacketEvent>, -) { - for event in events.read() { - if let Ok(mut chunk_batch_info) = query.get_mut(event.entity) { - chunk_batch_info.batch_finished(event.batch_size); - let desired_chunks_per_tick = chunk_batch_info.desired_chunks_per_tick(); - send_packets.send(SendPacketEvent::new( - event.entity, - ServerboundChunkBatchReceived { - desired_chunks_per_tick, - }, - )); - } - } -} - -#[derive(Clone, Debug)] -pub struct ChunkReceiveSpeedAccumulator { - batch_sizes: Vec<u32>, - /// as milliseconds - batch_durations: Vec<u32>, - index: usize, - filled_size: usize, -} -impl ChunkReceiveSpeedAccumulator { - pub fn new(capacity: usize) -> Self { - Self { - batch_sizes: vec![0; capacity], - batch_durations: vec![0; capacity], - index: 0, - filled_size: 0, - } - } - - pub fn accumulate(&mut self, batch_size: u32, batch_duration: Duration) { - self.batch_sizes[self.index] = batch_size; - self.batch_durations[self.index] = - f32::clamp(batch_duration.as_millis() as f32, 0., 15000.) as u32; - self.index = (self.index + 1) % self.batch_sizes.len(); - if self.filled_size < self.batch_sizes.len() { - self.filled_size += 1; - } - } - - pub fn get_millis_per_chunk(&self) -> f64 { - let mut total_batch_size = 0; - let mut total_batch_duration = 0; - for i in 0..self.filled_size { - total_batch_size += self.batch_sizes[i]; - total_batch_duration += self.batch_durations[i]; - } - if total_batch_size == 0 { - return 0.; - } - total_batch_duration as f64 / total_batch_size as f64 - } -} - -impl Default for ChunkBatchInfo { - fn default() -> Self { - Self { - start_time: Instant::now(), - aggregated_duration_per_chunk: Duration::from_millis(2), - old_samples_weight: 1, - } - } -} |
