aboutsummaryrefslogtreecommitdiff
path: root/azalea-physics/tests
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-01-10 16:45:27 -0600
committerGitHub <noreply@github.com>2025-01-10 16:45:27 -0600
commit0d16f01571ec8315f3979eae46981e559ade1cf9 (patch)
treeea43c32a57b0e6a67579d75a134dfbc009d09781 /azalea-physics/tests
parent615d8f9d2ac56b3244d328587243301da253eafd (diff)
downloadazalea-drasl-0d16f01571ec8315f3979eae46981e559ade1cf9.tar.xz
Fluid physics (#199)
* start implementing fluid physics * Initial implementation of fluid pushing * different travel function in water * bubble columns * jumping in water * cleanup * change ultrawarm to be required * fix for clippy
Diffstat (limited to 'azalea-physics/tests')
-rw-r--r--azalea-physics/tests/physics.rs365
1 files changed, 365 insertions, 0 deletions
diff --git a/azalea-physics/tests/physics.rs b/azalea-physics/tests/physics.rs
new file mode 100644
index 00000000..c7e85006
--- /dev/null
+++ b/azalea-physics/tests/physics.rs
@@ -0,0 +1,365 @@
+use azalea_core::{
+ position::{BlockPos, ChunkPos, Vec3},
+ resource_location::ResourceLocation,
+ tick::GameTick,
+};
+use azalea_entity::{EntityBundle, EntityPlugin, LocalEntity, Physics, Position};
+use azalea_physics::PhysicsPlugin;
+use azalea_world::{Chunk, InstanceContainer, MinecraftEntityId, PartialInstance};
+use bevy_app::App;
+use uuid::Uuid;
+
+/// You need an app to spawn entities in the world and do updates.
+fn make_test_app() -> App {
+ let mut app = App::new();
+ app.add_plugins((PhysicsPlugin, EntityPlugin))
+ .init_resource::<InstanceContainer>();
+ app
+}
+
+#[test]
+fn test_gravity() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+ // the entity has to be in a loaded chunk for physics to work
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.,
+ y: 70.,
+ z: 0.,
+ },
+ azalea_registry::EntityKind::Zombie,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ {
+ let entity_pos = *app.world_mut().get::<Position>(entity).unwrap();
+ // y should start at 70
+ assert_eq!(entity_pos.y, 70.);
+ }
+ app.update();
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ {
+ let entity_pos = *app.world_mut().get::<Position>(entity).unwrap();
+ // delta is applied before gravity, so the first tick only sets the delta
+ assert_eq!(entity_pos.y, 70.);
+ let entity_physics = app.world_mut().get::<Physics>(entity).unwrap();
+ assert!(entity_physics.velocity.y < 0.);
+ }
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ {
+ let entity_pos = *app.world_mut().get::<Position>(entity).unwrap();
+ // the second tick applies the delta to the position, so now it should go down
+ assert!(
+ entity_pos.y < 70.,
+ "Entity y ({}) didn't go down after physics steps",
+ entity_pos.y
+ );
+ }
+}
+#[test]
+fn test_collision() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 70.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ let block_state = partial_world.chunks.set_block_state(
+ &BlockPos { x: 0, y: 69, z: 0 },
+ azalea_registry::Block::Stone.into(),
+ &world_lock.write().chunks,
+ );
+ assert!(
+ block_state.is_some(),
+ "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
+ );
+ app.update();
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ {
+ let entity_pos = *app.world_mut().get::<Position>(entity).unwrap();
+ // delta will change, but it won't move until next tick
+ assert_eq!(entity_pos.y, 70.);
+ let entity_physics = app.world_mut().get::<Physics>(entity).unwrap();
+ assert!(entity_physics.velocity.y < 0.);
+ }
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ {
+ let entity_pos = *app.world_mut().get::<Position>(entity).unwrap();
+ // the second tick applies the delta to the position, but it also does collision
+ assert_eq!(entity_pos.y, 70.);
+ }
+}
+
+#[test]
+fn test_slab_collision() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 71.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ let block_state = partial_world.chunks.set_block_state(
+ &BlockPos { x: 0, y: 69, z: 0 },
+ azalea_block::blocks::StoneSlab {
+ kind: azalea_block::properties::Type::Bottom,
+ waterlogged: false,
+ }
+ .into(),
+ &world_lock.write().chunks,
+ );
+ assert!(
+ block_state.is_some(),
+ "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
+ );
+ // do a few steps so we fall on the slab
+ for _ in 0..20 {
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ }
+ let entity_pos = app.world_mut().get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 69.5);
+}
+
+#[test]
+fn test_top_slab_collision() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 71.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ let block_state = world_lock.write().chunks.set_block_state(
+ &BlockPos { x: 0, y: 69, z: 0 },
+ azalea_block::blocks::StoneSlab {
+ kind: azalea_block::properties::Type::Top,
+ waterlogged: false,
+ }
+ .into(),
+ );
+ assert!(
+ block_state.is_some(),
+ "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
+ );
+ // do a few steps so we fall on the slab
+ for _ in 0..20 {
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ }
+ let entity_pos = app.world_mut().get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 70.);
+}
+
+#[test]
+fn test_weird_wall_collision() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 73.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ let block_state = world_lock.write().chunks.set_block_state(
+ &BlockPos { x: 0, y: 69, z: 0 },
+ azalea_block::blocks::CobblestoneWall {
+ east: azalea_block::properties::WallEast::Low,
+ north: azalea_block::properties::WallNorth::Low,
+ south: azalea_block::properties::WallSouth::Low,
+ west: azalea_block::properties::WallWest::Low,
+ up: false,
+ waterlogged: false,
+ }
+ .into(),
+ );
+ assert!(
+ block_state.is_some(),
+ "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
+ );
+ // do a few steps so we fall on the wall
+ for _ in 0..20 {
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ }
+
+ let entity_pos = app.world_mut().get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 70.5);
+}
+
+#[test]
+fn test_negative_coordinates_weird_wall_collision() {
+ let mut app = make_test_app();
+ let world_lock = app.world_mut().resource_mut::<InstanceContainer>().insert(
+ ResourceLocation::new("minecraft:overworld"),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialInstance::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: -1, z: -1 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world_mut()
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: -7.5,
+ y: 73.,
+ z: -7.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld"),
+ ),
+ MinecraftEntityId(0),
+ LocalEntity,
+ ))
+ .id();
+ let block_state = world_lock.write().chunks.set_block_state(
+ &BlockPos {
+ x: -8,
+ y: 69,
+ z: -8,
+ },
+ azalea_block::blocks::CobblestoneWall {
+ east: azalea_block::properties::WallEast::Low,
+ north: azalea_block::properties::WallNorth::Low,
+ south: azalea_block::properties::WallSouth::Low,
+ west: azalea_block::properties::WallWest::Low,
+ up: false,
+ waterlogged: false,
+ }
+ .into(),
+ );
+ assert!(
+ block_state.is_some(),
+ "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
+ );
+ // do a few steps so we fall on the wall
+ for _ in 0..20 {
+ app.world_mut().run_schedule(GameTick);
+ app.update();
+ }
+
+ let entity_pos = app.world_mut().get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 70.5);
+}