mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
Fluid physics fixes (#210)
* start fixing code related to fluid physics * implement force_solid for blocks * afk pool test
This commit is contained in:
parent
a95408cbcc
commit
b0bd992adc
24 changed files with 888 additions and 401 deletions
|
@ -56,7 +56,6 @@ impl BlockBehavior {
|
|||
self
|
||||
}
|
||||
|
||||
// TODO: currently unused
|
||||
pub fn force_solid(mut self, force_solid: bool) -> Self {
|
||||
self.force_solid = Some(force_solid);
|
||||
self
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -106,9 +106,7 @@ where
|
|||
fn find(&self, ecs_lock: Arc<Mutex<World>>) -> Option<Entity> {
|
||||
let mut ecs = ecs_lock.lock();
|
||||
let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
|
||||
let entity = query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e);
|
||||
|
||||
entity
|
||||
query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ impl Plugin for BrandPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_end_login_state(
|
||||
pub fn handle_end_login_state(
|
||||
mut removed: RemovedComponents<InLoginState>,
|
||||
query: Query<&ClientInformation>,
|
||||
mut send_packet_events: EventWriter<SendConfigPacketEvent>,
|
||||
|
|
|
@ -26,26 +26,26 @@ pub struct ClipPointOpts<'a> {
|
|||
}
|
||||
|
||||
impl AABB {
|
||||
pub fn contract(&self, x: f64, y: f64, z: f64) -> AABB {
|
||||
pub fn contract(&self, amount: Vec3) -> AABB {
|
||||
let mut min = self.min;
|
||||
let mut max = self.max;
|
||||
|
||||
if x < 0.0 {
|
||||
min.x -= x;
|
||||
} else if x > 0.0 {
|
||||
max.x -= x;
|
||||
if amount.x < 0.0 {
|
||||
min.x -= amount.x;
|
||||
} else if amount.x > 0.0 {
|
||||
max.x -= amount.x;
|
||||
}
|
||||
|
||||
if y < 0.0 {
|
||||
min.y -= y;
|
||||
} else if y > 0.0 {
|
||||
max.y -= y;
|
||||
if amount.y < 0.0 {
|
||||
min.y -= amount.y;
|
||||
} else if amount.y > 0.0 {
|
||||
max.y -= amount.y;
|
||||
}
|
||||
|
||||
if z < 0.0 {
|
||||
min.z -= z;
|
||||
} else if z > 0.0 {
|
||||
max.z -= z;
|
||||
if amount.z < 0.0 {
|
||||
min.z -= amount.z;
|
||||
} else if amount.z > 0.0 {
|
||||
max.z -= amount.z;
|
||||
}
|
||||
|
||||
AABB { min, max }
|
||||
|
@ -84,20 +84,23 @@ impl AABB {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inflate(&self, x: f64, y: f64, z: f64) -> AABB {
|
||||
let min_x = self.min.x - x;
|
||||
let min_y = self.min.y - y;
|
||||
let min_z = self.min.z - z;
|
||||
pub fn inflate(&self, amount: Vec3) -> AABB {
|
||||
let min_x = self.min.x - amount.x;
|
||||
let min_y = self.min.y - amount.y;
|
||||
let min_z = self.min.z - amount.z;
|
||||
|
||||
let max_x = self.max.x + x;
|
||||
let max_y = self.max.y + y;
|
||||
let max_z = self.max.z + z;
|
||||
let max_x = self.max.x + amount.x;
|
||||
let max_y = self.max.y + amount.y;
|
||||
let max_z = self.max.z + amount.z;
|
||||
|
||||
AABB {
|
||||
min: Vec3::new(min_x, min_y, min_z),
|
||||
max: Vec3::new(max_x, max_y, max_z),
|
||||
}
|
||||
}
|
||||
pub fn inflate_all(&self, amount: f64) -> AABB {
|
||||
self.inflate(Vec3::new(amount, amount, amount))
|
||||
}
|
||||
|
||||
pub fn intersect(&self, other: &AABB) -> AABB {
|
||||
let min_x = self.min.x.max(other.min.x);
|
||||
|
@ -144,17 +147,17 @@ impl AABB {
|
|||
&& self.min.z < other.max.z
|
||||
&& self.max.z > other.min.z
|
||||
}
|
||||
pub fn intersects_vec3(&self, other: &Vec3, other2: &Vec3) -> bool {
|
||||
pub fn intersects_vec3(&self, corner1: &Vec3, corner2: &Vec3) -> bool {
|
||||
self.intersects_aabb(&AABB {
|
||||
min: Vec3::new(
|
||||
other.x.min(other2.x),
|
||||
other.y.min(other2.y),
|
||||
other.z.min(other2.z),
|
||||
corner1.x.min(corner2.x),
|
||||
corner1.y.min(corner2.y),
|
||||
corner1.z.min(corner2.z),
|
||||
),
|
||||
max: Vec3::new(
|
||||
other.x.max(other2.x),
|
||||
other.y.max(other2.y),
|
||||
other.z.max(other2.z),
|
||||
corner1.x.max(corner2.x),
|
||||
corner1.y.max(corner2.y),
|
||||
corner1.z.max(corner2.z),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
@ -183,12 +186,11 @@ impl AABB {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn deflate(&mut self, x: f64, y: f64, z: f64) -> AABB {
|
||||
self.inflate(-x, -y, -z)
|
||||
pub fn deflate(&self, amount: Vec3) -> AABB {
|
||||
self.inflate(Vec3::new(-amount.x, -amount.y, -amount.z))
|
||||
}
|
||||
|
||||
pub fn deflate_all(&mut self, amount: f64) -> AABB {
|
||||
self.deflate(amount, amount, amount)
|
||||
pub fn deflate_all(&self, amount: f64) -> AABB {
|
||||
self.deflate(Vec3::new(amount, amount, amount))
|
||||
}
|
||||
|
||||
pub fn clip(&self, min: &Vec3, max: &Vec3) -> Option<Vec3> {
|
||||
|
@ -434,11 +436,11 @@ impl AABB {
|
|||
let new_center = center + vector;
|
||||
|
||||
for aabb in boxes {
|
||||
let inflated = aabb.inflate(
|
||||
let inflated = aabb.inflate(Vec3::new(
|
||||
self.get_size(Axis::X) * 0.5,
|
||||
self.get_size(Axis::Y) * 0.5,
|
||||
self.get_size(Axis::Z) * 0.5,
|
||||
);
|
||||
));
|
||||
if inflated.contains(&new_center) || inflated.contains(¢er) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ impl Vec3 {
|
|||
|
||||
pub fn normalize(&self) -> Vec3 {
|
||||
let length = f64::sqrt(self.x * self.x + self.y * self.y + self.z * self.z);
|
||||
if length < 1e-4 {
|
||||
return Vec3::default();
|
||||
if length < 1e-5 {
|
||||
return Vec3::ZERO;
|
||||
}
|
||||
Vec3 {
|
||||
x: self.x / length,
|
||||
|
|
|
@ -17,9 +17,9 @@ pub enum Direction {
|
|||
impl Direction {
|
||||
pub const HORIZONTAL: [Direction; 4] = [
|
||||
Direction::North,
|
||||
Direction::East,
|
||||
Direction::South,
|
||||
Direction::West,
|
||||
Direction::East,
|
||||
];
|
||||
pub const VERTICAL: [Direction; 2] = [Direction::Down, Direction::Up];
|
||||
|
||||
|
|
|
@ -309,6 +309,21 @@ impl Vec3 {
|
|||
let z = self.z * (x_delta as f64) - self.x * (y_delta as f64);
|
||||
Vec3 { x, y, z }
|
||||
}
|
||||
|
||||
pub fn to_block_pos_floor(&self) -> BlockPos {
|
||||
BlockPos {
|
||||
x: self.x.floor() as i32,
|
||||
y: self.y.floor() as i32,
|
||||
z: self.z.floor() as i32,
|
||||
}
|
||||
}
|
||||
pub fn to_block_pos_ceil(&self) -> BlockPos {
|
||||
BlockPos {
|
||||
x: self.x.ceil() as i32,
|
||||
y: self.y.ceil() as i32,
|
||||
z: self.z.ceil() as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The coordinates of a block in the world. For entities (if the coordinate
|
||||
|
@ -600,6 +615,16 @@ impl From<ChunkSectionPos> for ChunkPos {
|
|||
ChunkPos { x: pos.x, z: pos.z }
|
||||
}
|
||||
}
|
||||
impl From<&Vec3> for ChunkSectionPos {
|
||||
fn from(pos: &Vec3) -> Self {
|
||||
ChunkSectionPos::from(&BlockPos::from(pos))
|
||||
}
|
||||
}
|
||||
impl From<Vec3> for ChunkSectionPos {
|
||||
fn from(pos: Vec3) -> Self {
|
||||
ChunkSectionPos::from(&pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BlockPos> for ChunkBlockPos {
|
||||
#[inline]
|
||||
|
|
|
@ -209,8 +209,8 @@ impl From<&LastSentPosition> for BlockPos {
|
|||
///
|
||||
/// If this is true, the entity will try to jump every tick. It's equivalent to
|
||||
/// the space key being held in vanilla.
|
||||
#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default)]
|
||||
pub struct Jumping(bool);
|
||||
#[derive(Debug, Component, Copy, Clone, Deref, DerefMut, Default, PartialEq, Eq)]
|
||||
pub struct Jumping(pub bool);
|
||||
|
||||
/// A component that contains the direction an entity is looking.
|
||||
#[derive(Debug, Component, Copy, Clone, Default, PartialEq, AzBuf)]
|
||||
|
|
|
@ -11,9 +11,7 @@ use azalea_core::{
|
|||
math::{self, EPSILON, lerp},
|
||||
position::{BlockPos, Vec3},
|
||||
};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_world::ChunkStorage;
|
||||
use bevy_ecs::entity::Entity;
|
||||
|
||||
use crate::collision::{BlockWithShape, EMPTY_SHAPE, VoxelShape};
|
||||
|
||||
|
@ -92,15 +90,6 @@ impl FluidPickType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EntityCollisionContext {
|
||||
pub descending: bool,
|
||||
pub entity_bottom: f64,
|
||||
pub held_item: ItemStack,
|
||||
// pub can_stand_on_fluid: Box<dyn Fn(&FluidState) -> bool>,
|
||||
pub entity: Entity,
|
||||
}
|
||||
|
||||
pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResult {
|
||||
traverse_blocks(
|
||||
context.from,
|
||||
|
|
97
azalea-physics/src/collision/entity_collisions.rs
Normal file
97
azalea-physics/src/collision/entity_collisions.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use azalea_core::aabb::AABB;
|
||||
use azalea_entity::{
|
||||
LocalEntity, Physics,
|
||||
metadata::{AbstractBoat, Shulker},
|
||||
};
|
||||
use azalea_world::Instance;
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
query::{Or, With, Without},
|
||||
system::Query,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use super::VoxelShape;
|
||||
|
||||
/// This query matches on entities that we can collide with. That is, boats and
|
||||
/// shulkers.
|
||||
///
|
||||
/// If you want to use this in a more complex query, use
|
||||
/// [`CollidableEntityFilter`] as a filter instead.
|
||||
pub type CollidableEntityQuery<'world, 'state> = Query<'world, 'state, (), CollidableEntityFilter>;
|
||||
/// This filter matches on entities that we can collide with (boats and
|
||||
/// shulkers).
|
||||
///
|
||||
/// Use [`CollidableEntityQuery`] if you want an empty query that matches with
|
||||
/// this.
|
||||
pub type CollidableEntityFilter = Or<(With<AbstractBoat>, With<Shulker>)>;
|
||||
|
||||
pub type PhysicsQuery<'world, 'state, 'a> =
|
||||
Query<'world, 'state, &'a Physics, Without<LocalEntity>>;
|
||||
|
||||
pub fn get_entity_collisions(
|
||||
world: &Instance,
|
||||
aabb: &AABB,
|
||||
source_entity: Option<Entity>,
|
||||
physics_query: &PhysicsQuery,
|
||||
collidable_entity_query: &CollidableEntityQuery,
|
||||
) -> Vec<VoxelShape> {
|
||||
if aabb.size() < 1.0E-7 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let collision_predicate = |entity| collidable_entity_query.get(entity).is_ok();
|
||||
|
||||
let collidable_entities = get_entities(
|
||||
world,
|
||||
source_entity,
|
||||
&aabb.inflate_all(1.0E-7),
|
||||
&collision_predicate,
|
||||
physics_query,
|
||||
);
|
||||
|
||||
collidable_entities
|
||||
.into_iter()
|
||||
.map(|(_entity, aabb)| VoxelShape::from(aabb))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return all entities that are colliding with the given bounding box and match
|
||||
/// the given predicate.
|
||||
///
|
||||
/// `source_entity` is the entity that the bounding box belongs to, and won't be
|
||||
/// one of the returned entities.
|
||||
pub fn get_entities(
|
||||
world: &Instance,
|
||||
source_entity: Option<Entity>,
|
||||
aabb: &AABB,
|
||||
predicate: &dyn Fn(Entity) -> bool,
|
||||
physics_query: &PhysicsQuery,
|
||||
) -> Vec<(Entity, AABB)> {
|
||||
let mut matches = Vec::new();
|
||||
|
||||
super::world_collisions::for_entities_in_chunks_colliding_with(
|
||||
world,
|
||||
aabb,
|
||||
|_chunk_pos, entities_in_chunk| {
|
||||
// now check if the entity itself collides
|
||||
for &candidate in entities_in_chunk {
|
||||
if Some(candidate) != source_entity && predicate(candidate) {
|
||||
let Ok(physics) = physics_query.get(candidate) else {
|
||||
error!(
|
||||
"Entity {candidate} (found from for_entities_in_chunks_colliding_with) is missing required components."
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let candidate_aabb = physics.bounding_box;
|
||||
if aabb.intersects_aabb(&candidate_aabb) {
|
||||
matches.push((candidate, physics.bounding_box));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
matches
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
mod blocks;
|
||||
mod discrete_voxel_shape;
|
||||
pub mod entity_collisions;
|
||||
mod mergers;
|
||||
mod shape;
|
||||
mod world_collisions;
|
||||
pub mod world_collisions;
|
||||
|
||||
use std::{ops::Add, sync::LazyLock};
|
||||
|
||||
|
@ -279,7 +280,7 @@ fn collide_bounding_box(
|
|||
// TODO: world border
|
||||
|
||||
let block_collisions =
|
||||
get_block_collisions(world, entity_bounding_box.expand_towards(movement));
|
||||
get_block_collisions(world, &entity_bounding_box.expand_towards(movement));
|
||||
collision_boxes.extend(block_collisions);
|
||||
collide_with_shapes(movement, *entity_bounding_box, &collision_boxes)
|
||||
}
|
||||
|
@ -392,6 +393,11 @@ fn calculate_shape_for_fluid(amount: u8) -> VoxelShape {
|
|||
///
|
||||
/// This is marked as deprecated in Minecraft.
|
||||
pub fn legacy_blocks_motion(block: BlockState) -> bool {
|
||||
if block == BlockState::AIR {
|
||||
// fast path
|
||||
return false;
|
||||
}
|
||||
|
||||
let registry_block = azalea_registry::Block::from(block);
|
||||
legacy_calculate_solid(block)
|
||||
&& registry_block != azalea_registry::Block::Cobweb
|
||||
|
|
|
@ -194,7 +194,7 @@ impl Shapes {
|
|||
}
|
||||
|
||||
/// Check if the op is true anywhere when joining the two shapes
|
||||
/// vanilla calls this joinIsNotEmpty
|
||||
/// vanilla calls this joinIsNotEmpty (join_is_not_empty).
|
||||
pub fn matches_anywhere(
|
||||
a: &VoxelShape,
|
||||
b: &VoxelShape,
|
||||
|
@ -574,13 +574,18 @@ impl VoxelShape {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<AABB> for VoxelShape {
|
||||
fn from(aabb: AABB) -> Self {
|
||||
impl From<&AABB> for VoxelShape {
|
||||
fn from(aabb: &AABB) -> Self {
|
||||
box_shape(
|
||||
aabb.min.x, aabb.min.y, aabb.min.z, aabb.max.x, aabb.max.y, aabb.max.z,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl From<AABB> for VoxelShape {
|
||||
fn from(aabb: AABB) -> Self {
|
||||
VoxelShape::from(&aabb)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct ArrayVoxelShape {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
use std::sync::Arc;
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
use azalea_block::BlockState;
|
||||
use azalea_block::{BlockState, fluid_state::FluidState};
|
||||
use azalea_core::{
|
||||
cursor3d::{Cursor3d, CursorIterationType},
|
||||
cursor3d::{Cursor3d, CursorIteration, CursorIterationType},
|
||||
math::EPSILON,
|
||||
position::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos},
|
||||
position::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos, Vec3},
|
||||
};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_world::{Chunk, Instance};
|
||||
use bevy_ecs::entity::Entity;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{BLOCK_SHAPE, Shapes};
|
||||
use crate::collision::{AABB, BlockWithShape, VoxelShape};
|
||||
|
||||
pub fn get_block_collisions(world: &Instance, aabb: AABB) -> Vec<VoxelShape> {
|
||||
let mut state = BlockCollisionsState::new(world, aabb);
|
||||
pub fn get_block_collisions(world: &Instance, aabb: &AABB) -> Vec<VoxelShape> {
|
||||
let mut state = BlockCollisionsState::new(world, aabb, EntityCollisionContext::of(None));
|
||||
let mut block_collisions = Vec::new();
|
||||
|
||||
let initial_chunk_pos = ChunkPos::from(state.cursor.origin());
|
||||
|
@ -21,52 +23,36 @@ pub fn get_block_collisions(world: &Instance, aabb: AABB) -> Vec<VoxelShape> {
|
|||
let initial_chunk = initial_chunk.as_deref().map(RwLock::read);
|
||||
|
||||
while let Some(item) = state.cursor.next() {
|
||||
if item.iteration_type == CursorIterationType::Corner {
|
||||
continue;
|
||||
}
|
||||
state.compute_next(
|
||||
item,
|
||||
&mut block_collisions,
|
||||
initial_chunk_pos,
|
||||
initial_chunk.as_deref(),
|
||||
);
|
||||
}
|
||||
|
||||
let item_chunk_pos = ChunkPos::from(item.pos);
|
||||
let block_state: BlockState = if item_chunk_pos == initial_chunk_pos {
|
||||
match &initial_chunk {
|
||||
Some(initial_chunk) => initial_chunk
|
||||
.get(&ChunkBlockPos::from(item.pos), state.world.chunks.min_y)
|
||||
.unwrap_or(BlockState::AIR),
|
||||
_ => BlockState::AIR,
|
||||
}
|
||||
} else {
|
||||
state.get_block_state(item.pos)
|
||||
};
|
||||
block_collisions
|
||||
}
|
||||
|
||||
if block_state.is_air() {
|
||||
// fast path since we can't collide with air
|
||||
continue;
|
||||
}
|
||||
pub fn get_block_and_liquid_collisions(world: &Instance, aabb: &AABB) -> Vec<VoxelShape> {
|
||||
let mut state = BlockCollisionsState::new(
|
||||
world,
|
||||
aabb,
|
||||
EntityCollisionContext::of(None).with_include_liquids(true),
|
||||
);
|
||||
let mut block_collisions = Vec::new();
|
||||
|
||||
// TODO: continue if self.only_suffocating_blocks and the block is not
|
||||
// suffocating
|
||||
let initial_chunk_pos = ChunkPos::from(state.cursor.origin());
|
||||
let initial_chunk = world.chunks.get(&initial_chunk_pos);
|
||||
let initial_chunk = initial_chunk.as_deref().map(RwLock::read);
|
||||
|
||||
// if it's a full block do a faster collision check
|
||||
if block_state.is_collision_shape_full() {
|
||||
if !state.aabb.intersects_aabb(&AABB {
|
||||
min: item.pos.to_vec3_floored(),
|
||||
max: (item.pos + 1).to_vec3_floored(),
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
block_collisions.push(BLOCK_SHAPE.move_relative(item.pos.to_vec3_floored()));
|
||||
continue;
|
||||
}
|
||||
|
||||
let block_shape = state.get_block_shape(block_state);
|
||||
|
||||
let block_shape = block_shape.move_relative(item.pos.to_vec3_floored());
|
||||
// if the entity shape and block shape don't collide, continue
|
||||
if !Shapes::matches_anywhere(&block_shape, &state.entity_shape, |a, b| a && b) {
|
||||
continue;
|
||||
}
|
||||
|
||||
block_collisions.push(block_shape);
|
||||
while let Some(item) = state.cursor.next() {
|
||||
state.compute_next(
|
||||
item,
|
||||
&mut block_collisions,
|
||||
initial_chunk_pos,
|
||||
initial_chunk.as_deref(),
|
||||
);
|
||||
}
|
||||
|
||||
block_collisions
|
||||
|
@ -74,16 +60,73 @@ pub fn get_block_collisions(world: &Instance, aabb: AABB) -> Vec<VoxelShape> {
|
|||
|
||||
pub struct BlockCollisionsState<'a> {
|
||||
pub world: &'a Instance,
|
||||
pub aabb: AABB,
|
||||
pub aabb: &'a AABB,
|
||||
pub entity_shape: VoxelShape,
|
||||
pub cursor: Cursor3d,
|
||||
|
||||
_context: EntityCollisionContext,
|
||||
|
||||
cached_sections: Vec<(ChunkSectionPos, azalea_world::Section)>,
|
||||
cached_block_shapes: Vec<(BlockState, &'static VoxelShape)>,
|
||||
}
|
||||
|
||||
impl<'a> BlockCollisionsState<'a> {
|
||||
pub fn new(world: &'a Instance, aabb: AABB) -> Self {
|
||||
fn compute_next(
|
||||
&mut self,
|
||||
item: CursorIteration,
|
||||
block_collisions: &mut Vec<VoxelShape>,
|
||||
initial_chunk_pos: ChunkPos,
|
||||
initial_chunk: Option<&Chunk>,
|
||||
) {
|
||||
if item.iteration_type == CursorIterationType::Corner {
|
||||
return;
|
||||
}
|
||||
|
||||
let item_chunk_pos = ChunkPos::from(item.pos);
|
||||
let block_state: BlockState = if item_chunk_pos == initial_chunk_pos {
|
||||
match &initial_chunk {
|
||||
Some(initial_chunk) => initial_chunk
|
||||
.get(&ChunkBlockPos::from(item.pos), self.world.chunks.min_y)
|
||||
.unwrap_or(BlockState::AIR),
|
||||
_ => BlockState::AIR,
|
||||
}
|
||||
} else {
|
||||
self.get_block_state(item.pos)
|
||||
};
|
||||
|
||||
if block_state.is_air() {
|
||||
// fast path since we can't collide with air
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: continue if self.only_suffocating_blocks and the block is not
|
||||
// suffocating
|
||||
|
||||
// if it's a full block do a faster collision check
|
||||
if block_state.is_collision_shape_full() {
|
||||
if !self.aabb.intersects_aabb(&AABB {
|
||||
min: item.pos.to_vec3_floored(),
|
||||
max: (item.pos + 1).to_vec3_floored(),
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
block_collisions.push(BLOCK_SHAPE.move_relative(item.pos.to_vec3_floored()));
|
||||
return;
|
||||
}
|
||||
|
||||
let block_shape = self.get_block_shape(block_state);
|
||||
|
||||
let block_shape = block_shape.move_relative(item.pos.to_vec3_floored());
|
||||
// if the entity shape and block shape don't collide, continue
|
||||
if !Shapes::matches_anywhere(&block_shape, &self.entity_shape, |a, b| a && b) {
|
||||
return;
|
||||
}
|
||||
|
||||
block_collisions.push(block_shape);
|
||||
}
|
||||
|
||||
pub fn new(world: &'a Instance, aabb: &'a AABB, context: EntityCollisionContext) -> Self {
|
||||
let origin = BlockPos {
|
||||
x: (aabb.min.x - EPSILON).floor() as i32 - 1,
|
||||
y: (aabb.min.y - EPSILON).floor() as i32 - 1,
|
||||
|
@ -104,6 +147,8 @@ impl<'a> BlockCollisionsState<'a> {
|
|||
entity_shape: VoxelShape::from(aabb),
|
||||
cursor,
|
||||
|
||||
_context: context,
|
||||
|
||||
cached_sections: Vec::new(),
|
||||
cached_block_shapes: Vec::new(),
|
||||
}
|
||||
|
@ -182,3 +227,78 @@ impl<'a> BlockCollisionsState<'a> {
|
|||
shape
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EntityCollisionContext {
|
||||
pub descending: bool,
|
||||
pub entity_bottom: f64,
|
||||
pub held_item: ItemStack,
|
||||
can_stand_on_fluid_predicate: CanStandOnFluidPredicate,
|
||||
pub entity: Option<Entity>,
|
||||
}
|
||||
|
||||
impl EntityCollisionContext {
|
||||
pub fn of(entity: Option<Entity>) -> Self {
|
||||
Self {
|
||||
descending: false,
|
||||
entity_bottom: 0.0,
|
||||
held_item: ItemStack::Empty,
|
||||
can_stand_on_fluid_predicate: CanStandOnFluidPredicate::PassToEntity,
|
||||
entity,
|
||||
}
|
||||
}
|
||||
pub fn with_include_liquids(mut self, include_liquids: bool) -> Self {
|
||||
self.can_stand_on_fluid_predicate = if include_liquids {
|
||||
CanStandOnFluidPredicate::AlwaysTrue
|
||||
} else {
|
||||
CanStandOnFluidPredicate::PassToEntity
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn can_stand_on_fluid(&self, above: &FluidState, target: &FluidState) -> bool {
|
||||
self.can_stand_on_fluid_predicate.matches(target) && !above.is_same_kind(target)
|
||||
}
|
||||
}
|
||||
|
||||
enum CanStandOnFluidPredicate {
|
||||
PassToEntity,
|
||||
AlwaysTrue,
|
||||
}
|
||||
impl CanStandOnFluidPredicate {
|
||||
pub fn matches(&self, _state: &FluidState) -> bool {
|
||||
match self {
|
||||
Self::AlwaysTrue => true,
|
||||
// minecraft sometimes returns true for striders here, false for every other entity
|
||||
// though
|
||||
Self::PassToEntity => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This basically gets all the chunks that an entity colliding with
|
||||
/// that bounding box could be in.
|
||||
///
|
||||
/// This is forEachAccessibleNonEmptySection in vanilla Minecraft because they
|
||||
/// sort entities into sections instead of just chunks. In theory this might be
|
||||
/// a performance loss for Azalea. If this ever turns out to be a bottleneck,
|
||||
/// then maybe you should try having it do that instead.
|
||||
pub fn for_entities_in_chunks_colliding_with(
|
||||
world: &Instance,
|
||||
aabb: &AABB,
|
||||
mut consumer: impl FnMut(ChunkPos, &HashSet<Entity>),
|
||||
) {
|
||||
let min_section = ChunkSectionPos::from(aabb.min - Vec3::new(2., 4., 2.));
|
||||
let max_section = ChunkSectionPos::from(aabb.max + Vec3::new(2., 0., 2.));
|
||||
|
||||
let min_chunk = ChunkPos::from(min_section);
|
||||
let max_chunk = ChunkPos::from(max_section);
|
||||
|
||||
for chunk_x in min_chunk.x..=max_chunk.x {
|
||||
for chunk_z in min_chunk.z..=max_chunk.z {
|
||||
let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
|
||||
if let Some(entities) = world.entities_by_chunk.get(&chunk_pos) {
|
||||
consumer(chunk_pos, entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ pub fn update_in_water_state_and_do_fluid_pushing(
|
|||
.expect("All entities with InLoadedChunk should be in a valid world");
|
||||
let world = world_lock.read();
|
||||
|
||||
// reset the heights since they're going to be set in
|
||||
// update_in_water_state_and_do_water_current_pushing
|
||||
physics.water_fluid_height = 0.;
|
||||
physics.lava_fluid_height = 0.;
|
||||
|
||||
|
@ -110,9 +112,9 @@ fn update_fluid_height_and_do_fluid_pushing(
|
|||
let mut additional_player_delta = Vec3::default();
|
||||
let mut num_fluids_being_touched = 0;
|
||||
|
||||
for cur_x in min_x..=max_x {
|
||||
for cur_y in min_y..=max_y {
|
||||
for cur_z in min_z..=max_z {
|
||||
for cur_x in min_x..max_x {
|
||||
for cur_y in min_y..max_y {
|
||||
for cur_z in min_z..max_z {
|
||||
let cur_pos = BlockPos::new(cur_x, cur_y, cur_z);
|
||||
let Some(fluid_at_cur_pos) = world.get_fluid_state(&cur_pos) else {
|
||||
continue;
|
||||
|
@ -184,42 +186,44 @@ pub fn get_fluid_flow(fluid: &FluidState, world: &Instance, pos: BlockPos) -> Ve
|
|||
let mut z_flow: f64 = 0.;
|
||||
let mut x_flow: f64 = 0.;
|
||||
|
||||
let cur_fluid_height = fluid.height();
|
||||
|
||||
for direction in Direction::HORIZONTAL {
|
||||
let adjacent_block_pos = pos.offset_with_direction(direction);
|
||||
let adjacent_fluid_state = world
|
||||
.get_fluid_state(&adjacent_block_pos)
|
||||
|
||||
let adjacent_block_state = world
|
||||
.get_block_state(&adjacent_block_pos)
|
||||
.unwrap_or_default();
|
||||
if fluid.affects_flow(&adjacent_fluid_state) {
|
||||
let mut adjacent_fluid_height = adjacent_fluid_state.height();
|
||||
let mut adjacent_height_difference: f32 = 0.;
|
||||
let adjacent_fluid_state = FluidState::from(adjacent_block_state);
|
||||
|
||||
if adjacent_fluid_height == 0. {
|
||||
if !legacy_blocks_motion(
|
||||
world
|
||||
.get_block_state(&adjacent_block_pos)
|
||||
.unwrap_or_default(),
|
||||
) {
|
||||
let block_pos_below_adjacent = adjacent_block_pos.down(1);
|
||||
let fluid_below_adjacent = world
|
||||
.get_fluid_state(&block_pos_below_adjacent)
|
||||
.unwrap_or_default();
|
||||
if !fluid.affects_flow(&adjacent_fluid_state) {
|
||||
continue;
|
||||
};
|
||||
let mut adjacent_fluid_height = adjacent_fluid_state.height();
|
||||
let mut adjacent_height_difference: f32 = 0.;
|
||||
|
||||
if fluid.affects_flow(&fluid_below_adjacent) {
|
||||
adjacent_fluid_height = fluid_below_adjacent.height();
|
||||
if adjacent_fluid_height > 0. {
|
||||
adjacent_height_difference =
|
||||
fluid.height() - (adjacent_fluid_height - 0.8888889);
|
||||
}
|
||||
if adjacent_fluid_height == 0. {
|
||||
if !legacy_blocks_motion(adjacent_block_state) {
|
||||
let block_pos_below_adjacent = adjacent_block_pos.down(1);
|
||||
let fluid_below_adjacent = world
|
||||
.get_fluid_state(&block_pos_below_adjacent)
|
||||
.unwrap_or_default();
|
||||
|
||||
if fluid.affects_flow(&fluid_below_adjacent) {
|
||||
adjacent_fluid_height = fluid_below_adjacent.height();
|
||||
if adjacent_fluid_height > 0. {
|
||||
adjacent_height_difference =
|
||||
cur_fluid_height - (adjacent_fluid_height - 0.8888889);
|
||||
}
|
||||
}
|
||||
} else if adjacent_fluid_height > 0. {
|
||||
adjacent_height_difference = fluid.height() - adjacent_fluid_height;
|
||||
}
|
||||
} else if adjacent_fluid_height > 0. {
|
||||
adjacent_height_difference = cur_fluid_height - adjacent_fluid_height;
|
||||
}
|
||||
|
||||
if adjacent_height_difference != 0. {
|
||||
x_flow += (direction.x() as f32 * adjacent_height_difference) as f64;
|
||||
z_flow += (direction.z() as f32 * adjacent_height_difference) as f64;
|
||||
}
|
||||
if adjacent_height_difference != 0. {
|
||||
x_flow += (direction.x() as f32 * adjacent_height_difference) as f64;
|
||||
z_flow += (direction.z() as f32 * adjacent_height_difference) as f64;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,44 +90,40 @@ pub fn ai_step(
|
|||
physics.velocity.z = 0.;
|
||||
}
|
||||
|
||||
if let Some(jumping) = jumping {
|
||||
if **jumping {
|
||||
// TODO: jumping in liquids and jump delay
|
||||
if jumping == Some(&Jumping(true)) {
|
||||
let fluid_height = if physics.is_in_lava() {
|
||||
physics.lava_fluid_height
|
||||
} else if physics.is_in_water() {
|
||||
physics.water_fluid_height
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
|
||||
let fluid_height = if physics.is_in_lava() {
|
||||
physics.lava_fluid_height
|
||||
} else if physics.is_in_water() {
|
||||
physics.water_fluid_height
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
let in_water = physics.is_in_water() && fluid_height > 0.;
|
||||
let fluid_jump_threshold = travel::fluid_jump_threshold();
|
||||
|
||||
let in_water = physics.is_in_water() && fluid_height > 0.;
|
||||
let fluid_jump_threshold = travel::fluid_jump_threshold();
|
||||
|
||||
if !in_water || physics.on_ground() && fluid_height <= fluid_jump_threshold {
|
||||
if !physics.is_in_lava()
|
||||
|| physics.on_ground() && fluid_height <= fluid_jump_threshold
|
||||
if !in_water || physics.on_ground() && fluid_height <= fluid_jump_threshold {
|
||||
if !physics.is_in_lava()
|
||||
|| physics.on_ground() && fluid_height <= fluid_jump_threshold
|
||||
{
|
||||
if (physics.on_ground() || in_water && fluid_height <= fluid_jump_threshold)
|
||||
&& physics.no_jump_delay == 0
|
||||
{
|
||||
if (physics.on_ground() || in_water && fluid_height <= fluid_jump_threshold)
|
||||
&& physics.no_jump_delay == 0
|
||||
{
|
||||
jump_from_ground(
|
||||
&mut physics,
|
||||
position,
|
||||
look_direction,
|
||||
sprinting,
|
||||
instance_name,
|
||||
&instance_container,
|
||||
);
|
||||
physics.no_jump_delay = 10;
|
||||
}
|
||||
} else {
|
||||
jump_in_liquid(&mut physics);
|
||||
jump_from_ground(
|
||||
&mut physics,
|
||||
position,
|
||||
look_direction,
|
||||
sprinting,
|
||||
instance_name,
|
||||
&instance_container,
|
||||
);
|
||||
physics.no_jump_delay = 10;
|
||||
}
|
||||
} else {
|
||||
jump_in_liquid(&mut physics);
|
||||
}
|
||||
} else {
|
||||
jump_in_liquid(&mut physics);
|
||||
}
|
||||
} else {
|
||||
physics.no_jump_delay = 0;
|
||||
|
@ -417,7 +413,6 @@ fn handle_relative_friction_and_calculate_movement(
|
|||
.unwrap_or_default()
|
||||
.into();
|
||||
|
||||
// TODO: powdered snow
|
||||
if **on_climbable || block_at_feet == azalea_registry::Block::PowderSnow {
|
||||
physics.velocity.y = 0.2;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use azalea_block::{Block, BlockState};
|
||||
use azalea_core::{aabb::AABB, position::Vec3};
|
||||
use azalea_block::{Block, BlockState, fluid_state::FluidState};
|
||||
use azalea_core::{
|
||||
aabb::AABB,
|
||||
position::{BlockPos, Vec3},
|
||||
};
|
||||
use azalea_entity::{
|
||||
Attributes, InLoadedChunk, Jumping, LocalEntity, LookDirection, OnClimbable, Physics, Pose,
|
||||
Position, metadata::Sprinting, move_relative,
|
||||
|
@ -9,7 +12,11 @@ use bevy_ecs::prelude::*;
|
|||
|
||||
use crate::{
|
||||
HandleRelativeFrictionAndCalculateMovementOpts,
|
||||
collision::{MoverType, move_colliding},
|
||||
collision::{
|
||||
MoverType, Shapes,
|
||||
entity_collisions::{CollidableEntityQuery, PhysicsQuery, get_entity_collisions},
|
||||
move_colliding,
|
||||
},
|
||||
get_block_pos_below_that_affects_movement, handle_relative_friction_and_calculate_movement,
|
||||
};
|
||||
|
||||
|
@ -19,6 +26,7 @@ use crate::{
|
|||
pub fn travel(
|
||||
mut query: Query<
|
||||
(
|
||||
Entity,
|
||||
&mut Physics,
|
||||
&mut LookDirection,
|
||||
&mut Position,
|
||||
|
@ -32,8 +40,11 @@ pub fn travel(
|
|||
(With<LocalEntity>, With<InLoadedChunk>),
|
||||
>,
|
||||
instance_container: Res<InstanceContainer>,
|
||||
physics_query: PhysicsQuery,
|
||||
collidable_entity_query: CollidableEntityQuery,
|
||||
) {
|
||||
for (
|
||||
entity,
|
||||
mut physics,
|
||||
direction,
|
||||
position,
|
||||
|
@ -59,13 +70,16 @@ pub fn travel(
|
|||
// !this.canStandOnFluid(fluidAtBlock)` here but it doesn't matter
|
||||
// for players
|
||||
travel_in_fluid(
|
||||
&world,
|
||||
entity,
|
||||
&mut physics,
|
||||
&direction,
|
||||
position,
|
||||
attributes,
|
||||
sprinting,
|
||||
on_climbable,
|
||||
&world,
|
||||
&physics_query,
|
||||
&collidable_entity_query,
|
||||
);
|
||||
} else {
|
||||
travel_in_air(
|
||||
|
@ -149,14 +163,18 @@ fn travel_in_air(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn travel_in_fluid(
|
||||
world: &Instance,
|
||||
entity: Entity,
|
||||
physics: &mut Physics,
|
||||
direction: &LookDirection,
|
||||
mut position: Mut<Position>,
|
||||
attributes: &Attributes,
|
||||
sprinting: Sprinting,
|
||||
on_climbable: &OnClimbable,
|
||||
world: &Instance,
|
||||
physics_query: &PhysicsQuery,
|
||||
collidable_entity_query: &CollidableEntityQuery,
|
||||
) {
|
||||
let moving_down = physics.velocity.y <= 0.;
|
||||
let y = position.y;
|
||||
|
@ -239,11 +257,13 @@ fn travel_in_fluid(
|
|||
let velocity = physics.velocity;
|
||||
if physics.horizontal_collision
|
||||
&& is_free(
|
||||
physics.bounding_box,
|
||||
world,
|
||||
velocity.x,
|
||||
velocity.y + 0.6 - position.y + y,
|
||||
velocity.z,
|
||||
entity,
|
||||
physics_query,
|
||||
collidable_entity_query,
|
||||
physics,
|
||||
physics.bounding_box,
|
||||
velocity.up(0.6).down(position.y).up(y),
|
||||
)
|
||||
{
|
||||
physics.velocity.y = 0.3;
|
||||
|
@ -276,14 +296,97 @@ fn get_fluid_falling_adjusted_movement(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_free(bounding_box: AABB, world: &Instance, x: f64, y: f64, z: f64) -> bool {
|
||||
// let bounding_box = bounding_box.move_relative(Vec3::new(x, y, z));
|
||||
fn is_free(
|
||||
world: &Instance,
|
||||
source_entity: Entity,
|
||||
physics_query: &PhysicsQuery,
|
||||
collidable_entity_query: &CollidableEntityQuery,
|
||||
entity_physics: &mut Physics,
|
||||
bounding_box: AABB,
|
||||
delta: Vec3,
|
||||
) -> bool {
|
||||
let bounding_box = bounding_box.move_relative(delta);
|
||||
|
||||
let _ = (bounding_box, world, x, y, z);
|
||||
no_collision(
|
||||
world,
|
||||
Some(source_entity),
|
||||
physics_query,
|
||||
collidable_entity_query,
|
||||
entity_physics,
|
||||
&bounding_box,
|
||||
false,
|
||||
) && !contains_any_liquid(world, bounding_box)
|
||||
}
|
||||
|
||||
// TODO: implement this, see Entity.isFree
|
||||
fn no_collision(
|
||||
world: &Instance,
|
||||
source_entity: Option<Entity>,
|
||||
physics_query: &PhysicsQuery,
|
||||
collidable_entity_query: &CollidableEntityQuery,
|
||||
entity_physics: &mut Physics,
|
||||
aabb: &AABB,
|
||||
include_liquid_collisions: bool,
|
||||
) -> bool {
|
||||
let collisions = if include_liquid_collisions {
|
||||
crate::collision::world_collisions::get_block_and_liquid_collisions(world, aabb)
|
||||
} else {
|
||||
crate::collision::world_collisions::get_block_collisions(world, aabb)
|
||||
};
|
||||
|
||||
true
|
||||
for collision in collisions {
|
||||
if !collision.is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !get_entity_collisions(
|
||||
world,
|
||||
aabb,
|
||||
source_entity,
|
||||
physics_query,
|
||||
collidable_entity_query,
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
false
|
||||
} else if source_entity.is_none() {
|
||||
true
|
||||
} else {
|
||||
let collision = border_collision(entity_physics, aabb);
|
||||
if let Some(collision) = collision {
|
||||
// !Shapes.joinIsNotEmpty(collision, Shapes.create(aabb), BooleanOp.AND);
|
||||
!Shapes::matches_anywhere(&collision.into(), &aabb.into(), |a, b| a && b)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn border_collision(_entity_physics: &Physics, _aabb: &AABB) -> Option<AABB> {
|
||||
// TODO: implement world border, see CollisionGetter.borderCollision
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn contains_any_liquid(world: &Instance, bounding_box: AABB) -> bool {
|
||||
let min = bounding_box.min.to_block_pos_floor();
|
||||
let max = bounding_box.max.to_block_pos_ceil();
|
||||
|
||||
for x in min.x..max.x {
|
||||
for y in min.y..max.y {
|
||||
for z in min.z..max.z {
|
||||
let block_state = world
|
||||
.chunks
|
||||
.get_block_state(&BlockPos::new(x, y, z))
|
||||
.unwrap_or_default();
|
||||
if !FluidState::from(block_state).is_empty() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn get_effective_gravity() -> f64 {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use azalea_block::{
|
||||
BlockState, block_state::BlockStateIntegerRepr, fluid_state::to_or_from_legacy_fluid_level,
|
||||
properties::WaterLevel,
|
||||
};
|
||||
use azalea_core::{
|
||||
position::{BlockPos, ChunkPos, Vec3},
|
||||
registry_holder::RegistryHolder,
|
||||
|
@ -409,3 +413,137 @@ fn spawn_and_unload_world() {
|
|||
app.world_mut().run_schedule(GameTick);
|
||||
app.update();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_afk_pool() {
|
||||
let mut app = make_test_app();
|
||||
let world_lock = insert_overworld(&mut app);
|
||||
let mut partial_world = PartialInstance::default();
|
||||
|
||||
partial_world.chunks.set(
|
||||
&ChunkPos { x: 0, z: 0 },
|
||||
Some(Chunk::default()),
|
||||
&mut world_lock.write().chunks,
|
||||
);
|
||||
let setblock = |x: i32, y: i32, z: i32, b: BlockState| {
|
||||
world_lock
|
||||
.write()
|
||||
.chunks
|
||||
.set_block_state(&BlockPos { x, y, z }, b);
|
||||
};
|
||||
|
||||
let stone = azalea_block::blocks::Stone {}.into();
|
||||
let sign = azalea_block::blocks::OakSign {
|
||||
rotation: azalea_block::properties::OakSignRotation::_0,
|
||||
waterlogged: false,
|
||||
}
|
||||
.into();
|
||||
let water = |level: u8| {
|
||||
BlockState::from(azalea_block::blocks::Water {
|
||||
level: WaterLevel::from(to_or_from_legacy_fluid_level(level) as BlockStateIntegerRepr),
|
||||
})
|
||||
};
|
||||
|
||||
let mut y = 69;
|
||||
|
||||
// first layer
|
||||
{
|
||||
setblock(1, y, 1, stone);
|
||||
setblock(2, y, 1, stone);
|
||||
setblock(3, y, 1, stone);
|
||||
setblock(3, y, 2, stone);
|
||||
setblock(3, y, 3, stone);
|
||||
setblock(2, y, 3, stone);
|
||||
setblock(1, y, 3, stone);
|
||||
setblock(1, y, 2, stone);
|
||||
}
|
||||
// second layer
|
||||
y += 1;
|
||||
{
|
||||
setblock(1, y, 0, stone);
|
||||
setblock(2, y, 0, stone);
|
||||
setblock(3, y, 0, stone);
|
||||
|
||||
setblock(0, y, 1, stone);
|
||||
setblock(0, y, 2, stone);
|
||||
setblock(0, y, 3, stone);
|
||||
|
||||
setblock(1, y, 4, stone);
|
||||
setblock(2, y, 4, stone);
|
||||
setblock(3, y, 4, stone);
|
||||
|
||||
setblock(4, y, 1, stone);
|
||||
setblock(4, y, 2, stone);
|
||||
setblock(4, y, 3, stone);
|
||||
|
||||
// middle block
|
||||
setblock(2, y, 2, stone);
|
||||
|
||||
// sign
|
||||
setblock(1, y, 1, sign);
|
||||
|
||||
// water
|
||||
setblock(1, y, 2, water(8));
|
||||
setblock(1, y, 3, water(7));
|
||||
setblock(2, y, 3, water(6));
|
||||
setblock(3, y, 3, water(5));
|
||||
setblock(3, y, 2, water(4));
|
||||
setblock(3, y, 1, water(3));
|
||||
setblock(2, y, 1, water(2));
|
||||
}
|
||||
// third layer
|
||||
y += 1;
|
||||
{
|
||||
setblock(1, y, 1, water(8));
|
||||
setblock(2, y, 1, sign);
|
||||
}
|
||||
|
||||
let entity = app
|
||||
.world_mut()
|
||||
.spawn((
|
||||
EntityBundle::new(
|
||||
Uuid::nil(),
|
||||
Vec3 {
|
||||
x: 3.5,
|
||||
y: 70.,
|
||||
z: 1.5,
|
||||
},
|
||||
azalea_registry::EntityKind::Player,
|
||||
ResourceLocation::new("minecraft:overworld"),
|
||||
),
|
||||
MinecraftEntityId(0),
|
||||
LocalEntity,
|
||||
))
|
||||
.id();
|
||||
|
||||
let mut blocks_visited = Vec::new();
|
||||
let mut loops_done = 0;
|
||||
|
||||
for _ in 0..300 {
|
||||
app.world_mut().run_schedule(GameTick);
|
||||
app.update();
|
||||
|
||||
let entity_pos = app.world_mut().get::<Position>(entity).unwrap();
|
||||
let entity_block_pos = BlockPos::from(entity_pos);
|
||||
|
||||
if !blocks_visited.contains(&entity_block_pos) {
|
||||
blocks_visited.push(entity_block_pos);
|
||||
|
||||
if blocks_visited.len() == 8 {
|
||||
loops_done += 1;
|
||||
blocks_visited.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
blocks_visited.into_iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
BlockPos::new(3, 70, 2),
|
||||
BlockPos::new(3, 70, 1),
|
||||
BlockPos::new(2, 70, 1),
|
||||
BlockPos::new(1, 70, 1),
|
||||
]
|
||||
);
|
||||
assert_eq!(loops_done, 1);
|
||||
}
|
||||
|
|
|
@ -296,7 +296,7 @@ impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> {
|
|||
|
||||
let _ = socks5_impl::client::connect(&mut stream, address, proxy.auth)
|
||||
.await
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
.map_err(io::Error::other)?;
|
||||
|
||||
Self::new_from_stream(stream.into_inner()).await
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use azalea_client::InConfigState;
|
||||
use azalea_client::chunks::handle_chunk_batch_finished_event;
|
||||
use azalea_client::inventory::InventorySet;
|
||||
use azalea_client::packet::config::SendConfigPacketEvent;
|
||||
use azalea_client::packet::config::{SendConfigPacketEvent, handle_send_packet_event};
|
||||
use azalea_client::packet::game::SendPacketEvent;
|
||||
use azalea_client::packet::{death_event_on_0_health, game::ResourcePackEvent};
|
||||
use azalea_client::respawn::perform_respawn;
|
||||
|
@ -23,7 +23,9 @@ impl Plugin for AcceptResourcePacksPlugin {
|
|||
.before(perform_respawn)
|
||||
.after(death_event_on_0_health)
|
||||
.after(handle_chunk_batch_finished_event)
|
||||
.after(InventorySet),
|
||||
.after(InventorySet)
|
||||
.before(handle_send_packet_event)
|
||||
.after(azalea_client::brand::handle_end_login_state),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,14 @@ def generate(version_id):
|
|||
'1.20.3-pre4', 'shapes')
|
||||
pixlyzer_block_datas = lib.extract.get_pixlyzer_data(
|
||||
'1.20.3-pre4', 'blocks')
|
||||
burger_data = lib.extract.get_burger_data_for_version(version_id)
|
||||
|
||||
block_states_report = lib.extract.get_block_states_report(version_id)
|
||||
registries = lib.extract.get_registries_report(version_id)
|
||||
ordered_blocks = lib.code.blocks.get_ordered_blocks(registries)
|
||||
|
||||
lib.code.blocks.generate_blocks(
|
||||
block_states_report, pixlyzer_block_datas, ordered_blocks)
|
||||
block_states_report, pixlyzer_block_datas, ordered_blocks, burger_data)
|
||||
|
||||
lib.code.shapes.generate_block_shapes(
|
||||
pixlyzer_block_datas, shape_datas['shapes'], shape_datas['aabbs'], block_states_report)
|
||||
|
|
|
@ -12,13 +12,15 @@ BLOCKS_RS_DIR = get_dir_location('../azalea-block/src/generated.rs')
|
|||
# - Block: Has properties and states.
|
||||
|
||||
|
||||
def generate_blocks(blocks_report: dict, pixlyzer_block_datas: dict, ordered_blocks: list[str]):
|
||||
def generate_blocks(blocks_report: dict, pixlyzer_block_datas: dict, ordered_blocks: list[str], burger_data: dict):
|
||||
with open(BLOCKS_RS_DIR, 'r') as f:
|
||||
existing_code = f.read().splitlines()
|
||||
|
||||
new_make_block_states_macro_code = []
|
||||
new_make_block_states_macro_code.append('make_block_states! {')
|
||||
|
||||
burger_block_datas = burger_data[0]['blocks']['block']
|
||||
|
||||
# Find properties
|
||||
properties = {}
|
||||
|
||||
|
@ -77,6 +79,7 @@ def generate_blocks(blocks_report: dict, pixlyzer_block_datas: dict, ordered_blo
|
|||
for block_id in ordered_blocks:
|
||||
block_data_report = blocks_report['minecraft:' + block_id]
|
||||
block_data_pixlyzer = pixlyzer_block_datas.get(f'minecraft:{block_id}', {})
|
||||
block_data_burger = burger_block_datas.get(block_id, {})
|
||||
|
||||
default_property_variants: dict[str, str] = {}
|
||||
for state in block_data_report['states']:
|
||||
|
@ -129,6 +132,14 @@ def generate_blocks(blocks_report: dict, pixlyzer_block_datas: dict, ordered_blo
|
|||
friction = block_data_pixlyzer.get('friction')
|
||||
if friction != None:
|
||||
behavior_constructor += f'.friction({friction})'
|
||||
|
||||
force_solid = None
|
||||
if block_data_burger.get('force_solid_on'):
|
||||
force_solid = 'true'
|
||||
elif block_data_burger.get('force_solid_off'):
|
||||
force_solid = 'false'
|
||||
if force_solid != None:
|
||||
behavior_constructor += f'.force_solid({force_solid})'
|
||||
|
||||
# TODO: use burger to generate the blockbehavior
|
||||
new_make_block_states_macro_code.append(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Extracting data from the Minecraft jars
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from lib.download import get_server_jar, get_burger, get_client_jar, get_pixlyzer, get_yarn_data, get_fabric_api_versions, get_fabric_loader_versions
|
||||
from lib.download import get_mappings_for_version, get_server_jar, get_burger, get_client_jar, get_pixlyzer, get_yarn_data, get_fabric_api_versions, get_fabric_loader_versions
|
||||
from lib.utils import get_dir_location, to_camel_case, upper_first_letter
|
||||
from zipfile import ZipFile
|
||||
import subprocess
|
||||
|
@ -53,19 +53,8 @@ python_command = None
|
|||
|
||||
|
||||
def determine_python_command():
|
||||
global python_command
|
||||
if python_command:
|
||||
return python_command
|
||||
|
||||
def try_python_command(version):
|
||||
return os.system(f'{version} --version') == 0
|
||||
|
||||
for version in (sys.executable, 'python3.9', 'python3.8', 'python3', 'python'):
|
||||
if try_python_command(version):
|
||||
python_command = version
|
||||
return version
|
||||
raise Exception(
|
||||
'Couldn\'t determine python command to use to run burger with!')
|
||||
return 'venv/bin/python'
|
||||
|
||||
|
||||
|
||||
def run_python_command_and_download_deps(command):
|
||||
|
@ -105,10 +94,14 @@ def get_burger_data_for_version(version_id: str):
|
|||
if not os.path.exists(get_dir_location(f'__cache__/burger-{version_id}.json')):
|
||||
get_burger()
|
||||
get_client_jar(version_id)
|
||||
get_mappings_for_version(version_id)
|
||||
|
||||
print('\033[92mRunning Burger...\033[m')
|
||||
run_python_command_and_download_deps(
|
||||
f'cd {get_dir_location("__cache__/Burger")} && {determine_python_command()} munch.py {get_dir_location("__cache__")}/client-{version_id}.jar --output {get_dir_location("__cache__")}/burger-{version_id}.json'
|
||||
f'cd {get_dir_location("__cache__/Burger")} && '\
|
||||
f'{determine_python_command()} munch.py {get_dir_location("__cache__")}/client-{version_id}.jar '\
|
||||
f'--output {get_dir_location("__cache__")}/burger-{version_id}.json '\
|
||||
f'--mappings {get_dir_location("__cache__")}/mappings-{version_id}.txt'
|
||||
)
|
||||
with open(get_dir_location(f'__cache__/burger-{version_id}.json'), 'r') as f:
|
||||
return json.load(f)
|
||||
|
|
|
@ -78,7 +78,6 @@ class Mappings:
|
|||
return self.classes[obfuscated_class_name]
|
||||
|
||||
def get_method(self, obfuscated_class_name, obfuscated_method_name, obfuscated_signature):
|
||||
# print(obfuscated_class_name, self.methods[obfuscated_class_name])
|
||||
return self.methods[obfuscated_class_name][f'{obfuscated_method_name}({obfuscated_signature})']
|
||||
|
||||
def get_field_type(self, obfuscated_class_name, obfuscated_field_name) -> str:
|
||||
|
|
Loading…
Add table
Reference in a new issue