aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/tests
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-02-21 13:43:56 -0600
committerGitHub <noreply@github.com>2025-02-21 13:43:56 -0600
commit833f306e8b8faddd232b5c736b2134ed08adcb6c (patch)
tree08a59009f3118d5c56891d6c4fb6a0293aa9e6fc /azalea-client/tests
parent63b1036a96c45b0fefc6ca2501f1cc479acc95de (diff)
downloadazalea-drasl-833f306e8b8faddd232b5c736b2134ed08adcb6c.tar.xz
Fix errors on switching dimensions (#204)
* Fix errors on switching dimensions * fix other tests * clippy * fix log feature in test_simulation * fix chunks oops
Diffstat (limited to 'azalea-client/tests')
-rw-r--r--azalea-client/tests/change_dimension_to_nether_and_back.rs147
-rw-r--r--azalea-client/tests/set_health_before_login.rs55
-rw-r--r--azalea-client/tests/simulation.rs237
3 files changed, 202 insertions, 237 deletions
diff --git a/azalea-client/tests/change_dimension_to_nether_and_back.rs b/azalea-client/tests/change_dimension_to_nether_and_back.rs
new file mode 100644
index 00000000..3ed623ef
--- /dev/null
+++ b/azalea-client/tests/change_dimension_to_nether_and_back.rs
@@ -0,0 +1,147 @@
+use azalea_client::{test_simulation::*, InConfigState};
+use azalea_core::{position::ChunkPos, resource_location::ResourceLocation};
+use azalea_entity::{metadata::Health, LocalEntity};
+use azalea_protocol::packets::{
+ config::{ClientboundFinishConfiguration, ClientboundRegistryData},
+ game::ClientboundSetHealth,
+ ConnectionProtocol,
+};
+use azalea_registry::DimensionType;
+use azalea_world::InstanceName;
+use bevy_log::tracing_subscriber;
+use simdnbt::owned::{NbtCompound, NbtTag};
+
+#[test]
+fn test_change_dimension_to_nether_and_back() {
+ let _ = tracing_subscriber::fmt::try_init();
+
+ let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
+ assert!(simulation.has_component::<InConfigState>());
+
+ simulation.receive_packet(ClientboundRegistryData {
+ registry_id: ResourceLocation::new("minecraft:dimension_type"),
+ entries: vec![
+ (
+ // this dimension should never be created. it just exists to make sure we're not
+ // hard-coding the dimension type id anywhere.
+ ResourceLocation::new("azalea:fakedimension"),
+ Some(NbtCompound::from_values(vec![
+ ("height".into(), NbtTag::Int(16)),
+ ("min_y".into(), NbtTag::Int(0)),
+ ])),
+ ),
+ (
+ ResourceLocation::new("minecraft:overworld"),
+ Some(NbtCompound::from_values(vec![
+ ("height".into(), NbtTag::Int(384)),
+ ("min_y".into(), NbtTag::Int(-64)),
+ ])),
+ ),
+ (
+ ResourceLocation::new("minecraft:nether"),
+ Some(NbtCompound::from_values(vec![
+ ("height".into(), NbtTag::Int(256)),
+ ("min_y".into(), NbtTag::Int(0)),
+ ])),
+ ),
+ ]
+ .into_iter()
+ .collect(),
+ });
+ simulation.tick();
+ simulation.receive_packet(ClientboundFinishConfiguration);
+ simulation.tick();
+
+ assert!(!simulation.has_component::<InConfigState>());
+ assert!(simulation.has_component::<LocalEntity>());
+
+ simulation.receive_packet(ClientboundSetHealth {
+ health: 15.,
+ food: 20,
+ saturation: 20.,
+ });
+ simulation.tick();
+ assert_eq!(*simulation.component::<Health>(), 15.);
+
+ //
+ // OVERWORLD
+ //
+
+ simulation.receive_packet(make_basic_login_packet(
+ DimensionType::new_raw(1), // overworld
+ ResourceLocation::new("azalea:a"),
+ ));
+ simulation.tick();
+
+ assert_eq!(
+ *simulation.component::<InstanceName>(),
+ ResourceLocation::new("azalea:a"),
+ "InstanceName should be azalea:a after setting dimension to that"
+ );
+
+ simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
+ simulation.tick();
+ // make sure the chunk exists
+ simulation
+ .chunk(ChunkPos::new(0, 0))
+ .expect("chunk should exist");
+
+ //
+ // NETHER
+ //
+
+ simulation.receive_packet(make_basic_respawn_packet(
+ DimensionType::new_raw(2), // nether
+ ResourceLocation::new("azalea:b"),
+ ));
+ simulation.tick();
+
+ assert!(
+ simulation.chunk(ChunkPos::new(0, 0)).is_none(),
+ "chunk should not exist immediately after changing dimensions"
+ );
+ assert_eq!(
+ *simulation.component::<InstanceName>(),
+ ResourceLocation::new("azalea:b"),
+ "InstanceName should be azalea:b after changing dimensions to that"
+ );
+
+ simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), 256 / 16));
+ simulation.tick();
+ // make sure the chunk exists
+ simulation
+ .chunk(ChunkPos::new(0, 0))
+ .expect("chunk should exist");
+ simulation.receive_packet(make_basic_respawn_packet(
+ DimensionType::new_raw(2), // nether
+ ResourceLocation::new("minecraft:nether"),
+ ));
+ simulation.tick();
+
+ //
+ // BACK TO OVERWORLD
+ //
+
+ simulation.receive_packet(make_basic_login_packet(
+ DimensionType::new_raw(1), // overworld
+ ResourceLocation::new("azalea:a"),
+ ));
+ simulation.tick();
+
+ assert_eq!(
+ *simulation.component::<InstanceName>(),
+ ResourceLocation::new("azalea:a"),
+ "InstanceName should be azalea:a after setting dimension back to that"
+ );
+ assert!(
+ simulation.chunk(ChunkPos::new(0, 0)).is_none(),
+ "chunk should not exist immediately after switching back to overworld"
+ );
+
+ simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
+ simulation.tick();
+ // make sure the chunk exists
+ simulation
+ .chunk(ChunkPos::new(0, 0))
+ .expect("chunk should exist");
+}
diff --git a/azalea-client/tests/set_health_before_login.rs b/azalea-client/tests/set_health_before_login.rs
new file mode 100644
index 00000000..d6b6c426
--- /dev/null
+++ b/azalea-client/tests/set_health_before_login.rs
@@ -0,0 +1,55 @@
+use azalea_client::{test_simulation::*, InConfigState};
+use azalea_core::resource_location::ResourceLocation;
+use azalea_entity::{metadata::Health, LocalEntity};
+use azalea_protocol::packets::{
+ config::{ClientboundFinishConfiguration, ClientboundRegistryData},
+ game::ClientboundSetHealth,
+ ConnectionProtocol,
+};
+use azalea_registry::DimensionType;
+use bevy_log::tracing_subscriber;
+use simdnbt::owned::{NbtCompound, NbtTag};
+
+#[test]
+fn test_set_health_before_login() {
+ let _ = tracing_subscriber::fmt::try_init();
+
+ let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
+ assert!(simulation.has_component::<InConfigState>());
+
+ simulation.receive_packet(ClientboundRegistryData {
+ registry_id: ResourceLocation::new("minecraft:dimension_type"),
+ entries: vec![(
+ ResourceLocation::new("minecraft:overworld"),
+ Some(NbtCompound::from_values(vec![
+ ("height".into(), NbtTag::Int(384)),
+ ("min_y".into(), NbtTag::Int(-64)),
+ ])),
+ )]
+ .into_iter()
+ .collect(),
+ });
+ simulation.tick();
+ simulation.receive_packet(ClientboundFinishConfiguration);
+ simulation.tick();
+
+ assert!(!simulation.has_component::<InConfigState>());
+ assert!(simulation.has_component::<LocalEntity>());
+
+ simulation.receive_packet(ClientboundSetHealth {
+ health: 15.,
+ food: 20,
+ saturation: 20.,
+ });
+ simulation.tick();
+ assert_eq!(*simulation.component::<Health>(), 15.);
+
+ simulation.receive_packet(make_basic_login_packet(
+ DimensionType::new_raw(0), // overworld
+ ResourceLocation::new("minecraft:overworld"),
+ ));
+ simulation.tick();
+
+ // health should stay the same
+ assert_eq!(*simulation.component::<Health>(), 15.);
+}
diff --git a/azalea-client/tests/simulation.rs b/azalea-client/tests/simulation.rs
deleted file mode 100644
index 593622aa..00000000
--- a/azalea-client/tests/simulation.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use std::{fmt::Debug, sync::Arc, time::Duration};
-
-use azalea_auth::game_profile::GameProfile;
-use azalea_client::{
- events::LocalPlayerEvents,
- raw_connection::{RawConnection, RawConnectionReader, RawConnectionWriter},
- ClientInformation, GameProfileComponent, InConfigState, InstanceHolder, LocalPlayerBundle,
-};
-use azalea_core::{
- game_type::{GameMode, OptionalGameType},
- resource_location::ResourceLocation,
- tick::GameTick,
-};
-use azalea_entity::{
- metadata::{Health, PlayerMetadataBundle},
- LocalEntity,
-};
-use azalea_protocol::packets::{
- common::CommonPlayerSpawnInfo,
- config::{ClientboundFinishConfiguration, ClientboundRegistryData},
- game::{ClientboundLogin, ClientboundSetHealth},
- ConnectionProtocol, Packet, ProtocolPacket,
-};
-use azalea_registry::DimensionType;
-use azalea_world::{Instance, MinecraftEntityId};
-use bevy_app::App;
-use bevy_app::PluginGroup;
-use bevy_ecs::{prelude::*, schedule::ExecutorKind};
-use bevy_log::{tracing_subscriber, LogPlugin};
-use parking_lot::{Mutex, RwLock};
-use simdnbt::owned::{NbtCompound, NbtTag};
-use tokio::{sync::mpsc, time::sleep};
-use uuid::Uuid;
-
-#[test]
-fn test_set_health_before_login() {
- let _ = tracing_subscriber::fmt::try_init();
-
- let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
- assert!(simulation.has_component::<InConfigState>());
-
- simulation.receive_packet(ClientboundRegistryData {
- registry_id: ResourceLocation::new("minecraft:dimension_type"),
- entries: vec![(
- ResourceLocation::new("minecraft:overworld"),
- Some(NbtCompound::from_values(vec![
- ("height".into(), NbtTag::Int(384)),
- ("min_y".into(), NbtTag::Int(-64)),
- ])),
- )]
- .into_iter()
- .collect(),
- });
- simulation.tick();
- simulation.receive_packet(ClientboundFinishConfiguration);
- simulation.tick();
-
- assert!(!simulation.has_component::<InConfigState>());
- assert!(simulation.has_component::<LocalEntity>());
-
- simulation.receive_packet(ClientboundSetHealth {
- health: 15.,
- food: 20,
- saturation: 20.,
- });
- simulation.tick();
- assert_eq!(*simulation.component::<Health>(), 15.);
-
- simulation.receive_packet(ClientboundLogin {
- player_id: MinecraftEntityId(0),
- hardcore: false,
- levels: vec![],
- max_players: 20,
- chunk_radius: 8,
- simulation_distance: 8,
- reduced_debug_info: false,
- show_death_screen: true,
- do_limited_crafting: false,
- common: CommonPlayerSpawnInfo {
- dimension_type: DimensionType::Overworld,
- dimension: ResourceLocation::new("minecraft:overworld"),
- seed: 0,
- game_type: GameMode::Survival,
- previous_game_type: OptionalGameType(None),
- is_debug: false,
- is_flat: false,
- last_death_location: None,
- portal_cooldown: 0,
- sea_level: 63,
- },
- enforces_secure_chat: false,
- });
- simulation.tick();
-
- // health should stay the same
- assert_eq!(*simulation.component::<Health>(), 15.);
-}
-
-pub fn create_local_player_bundle(
- entity: Entity,
- connection_protocol: ConnectionProtocol,
-) -> (
- LocalPlayerBundle,
- mpsc::UnboundedReceiver<Box<[u8]>>,
- Arc<Mutex<Vec<Box<[u8]>>>>,
- tokio::runtime::Runtime,
-) {
- // unused since we'll trigger ticks ourselves
- let (run_schedule_sender, _run_schedule_receiver) = tokio::sync::mpsc::unbounded_channel();
-
- let (outgoing_packets_sender, outgoing_packets_receiver) = mpsc::unbounded_channel();
- let incoming_packet_queue = Arc::new(Mutex::new(Vec::new()));
- let reader = RawConnectionReader {
- incoming_packet_queue: incoming_packet_queue.clone(),
- run_schedule_sender,
- };
- let writer = RawConnectionWriter {
- outgoing_packets_sender,
- };
-
- let rt = tokio::runtime::Runtime::new().unwrap();
-
- // the tasks can't die since that would make us send a DisconnectEvent
- let read_packets_task = rt.spawn(async {
- loop {
- sleep(Duration::from_secs(60)).await;
- }
- });
- let write_packets_task = rt.spawn(async {
- loop {
- sleep(Duration::from_secs(60)).await;
- }
- });
-
- let raw_connection = RawConnection {
- reader,
- writer,
- read_packets_task,
- write_packets_task,
- connection_protocol,
- };
-
- let (local_player_events_sender, _local_player_events_receiver) = mpsc::unbounded_channel();
-
- let instance = Instance::default();
- let instance_holder = InstanceHolder::new(entity, Arc::new(RwLock::new(instance)));
-
- let local_player_bundle = LocalPlayerBundle {
- raw_connection,
- local_player_events: LocalPlayerEvents(local_player_events_sender),
- game_profile: GameProfileComponent(GameProfile::new(Uuid::nil(), "azalea".to_owned())),
- client_information: ClientInformation::default(),
- instance_holder,
- metadata: PlayerMetadataBundle::default(),
- };
-
- (
- local_player_bundle,
- outgoing_packets_receiver,
- incoming_packet_queue,
- rt,
- )
-}
-
-fn create_simulation_app() -> App {
- let mut app = App::new();
- app.add_plugins(azalea_client::DefaultPlugins.build().disable::<LogPlugin>());
- app.edit_schedule(bevy_app::Main, |schedule| {
- // makes test results more reproducible
- schedule.set_executor_kind(ExecutorKind::SingleThreaded);
- });
- app
-}
-
-pub struct Simulation {
- pub app: App,
- pub entity: Entity,
-
- // the runtime needs to be kept around for the tasks to be considered alive
- pub rt: tokio::runtime::Runtime,
-
- pub incoming_packet_queue: Arc<Mutex<Vec<Box<[u8]>>>>,
- pub outgoing_packets_receiver: mpsc::UnboundedReceiver<Box<[u8]>>,
-}
-
-impl Simulation {
- pub fn new(initial_connection_protocol: ConnectionProtocol) -> Self {
- let mut app = create_simulation_app();
- let mut entity = app.world_mut().spawn_empty();
- let (player, outgoing_packets_receiver, incoming_packet_queue, rt) =
- create_local_player_bundle(entity.id(), initial_connection_protocol);
- entity.insert(player);
-
- let entity = entity.id();
-
- tick_app(&mut app);
-
- match initial_connection_protocol {
- ConnectionProtocol::Configuration => {
- app.world_mut().entity_mut(entity).insert(InConfigState);
- tick_app(&mut app);
- }
- _ => {}
- }
-
- Self {
- app,
- entity,
- rt,
- incoming_packet_queue,
- outgoing_packets_receiver,
- }
- }
-
- pub fn receive_packet<P: ProtocolPacket + Debug>(&mut self, packet: impl Packet<P>) {
- let buf = azalea_protocol::write::serialize_packet(&packet.into_variant()).unwrap();
- self.incoming_packet_queue.lock().push(buf.into());
- }
-
- pub fn tick(&mut self) {
- tick_app(&mut self.app);
- }
- pub fn component<T: Component + Clone>(&self) -> T {
- self.app.world().get::<T>(self.entity).unwrap().clone()
- }
- pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
- self.app.world().get::<T>(self.entity).cloned()
- }
- pub fn has_component<T: Component>(&self) -> bool {
- self.app.world().get::<T>(self.entity).is_some()
- }
-}
-
-fn tick_app(app: &mut App) {
- app.update();
- app.world_mut().run_schedule(GameTick);
-}