mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
fix parsing Dust particle and treat waterlogged blocks as liquid in pathfinder
This commit is contained in:
parent
de5a53ce08
commit
f03e0c2235
15 changed files with 183 additions and 54 deletions
|
@ -694,11 +694,8 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let last_state_id = state_id - 1;
|
||||
let mut generated = quote! {
|
||||
impl BlockState {
|
||||
/// Returns the highest possible state ID.
|
||||
#[inline]
|
||||
pub fn max_state() -> u32 {
|
||||
#last_state_id
|
||||
}
|
||||
/// The highest possible block state ID.
|
||||
pub const MAX_STATE: u32 = #last_state_id;
|
||||
|
||||
/// Get a property from this block state. Will be `None` if the block can't have the property.
|
||||
///
|
||||
|
|
|
@ -56,7 +56,7 @@ impl BlockState {
|
|||
|
||||
#[inline]
|
||||
pub fn is_valid_state(state_id: u32) -> bool {
|
||||
state_id <= Self::max_state()
|
||||
state_id <= Self::MAX_STATE
|
||||
}
|
||||
|
||||
/// Returns true if the block is air. This only checks for normal air, not
|
||||
|
@ -184,8 +184,8 @@ mod tests {
|
|||
fn test_from_u32() {
|
||||
assert_eq!(BlockState::try_from(0).unwrap(), BlockState::AIR);
|
||||
|
||||
assert!(BlockState::try_from(BlockState::max_state()).is_ok());
|
||||
assert!(BlockState::try_from(BlockState::max_state() + 1).is_err());
|
||||
assert!(BlockState::try_from(BlockState::MAX_STATE).is_ok());
|
||||
assert!(BlockState::try_from(BlockState::MAX_STATE + 1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -12,9 +12,9 @@ const ADDRESS_BITS_PER_WORD: usize = 6;
|
|||
|
||||
// the Index trait requires us to return a reference, but we can't do that
|
||||
impl BitSet {
|
||||
pub fn new(size: usize) -> Self {
|
||||
pub fn new(num_bits: usize) -> Self {
|
||||
BitSet {
|
||||
data: vec![0; size.div_ceil(64)],
|
||||
data: vec![0; num_bits.div_ceil(64)],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
55
azalea-core/src/color.rs
Normal file
55
azalea-core/src/color.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use azalea_buf::AzBuf;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, AzBuf)]
|
||||
pub struct RgbColor {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl RgbColor {
|
||||
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self {
|
||||
value: (r as u32) << 16 | (g as u32) << 8 | b as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn red(&self) -> u8 {
|
||||
(self.value >> 16) as u8
|
||||
}
|
||||
|
||||
pub fn green(&self) -> u8 {
|
||||
(self.value >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> u8 {
|
||||
self.value as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, AzBuf)]
|
||||
pub struct ArgbColor {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl ArgbColor {
|
||||
pub fn new(a: u8, r: u8, g: u8, b: u8) -> Self {
|
||||
Self {
|
||||
value: (a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | b as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> u8 {
|
||||
(self.value >> 24) as u8
|
||||
}
|
||||
|
||||
pub fn red(&self) -> u8 {
|
||||
(self.value >> 16) as u8
|
||||
}
|
||||
|
||||
pub fn green(&self) -> u8 {
|
||||
(self.value >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> u8 {
|
||||
self.value as u8
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
pub mod aabb;
|
||||
pub mod bitset;
|
||||
pub mod block_hit_result;
|
||||
pub mod color;
|
||||
pub mod cursor3d;
|
||||
pub mod delta;
|
||||
pub mod difficulty;
|
||||
|
|
|
@ -71,6 +71,7 @@ macro_rules! vec3_impl {
|
|||
|
||||
/// Return a new instance of this position with the z coordinate subtracted
|
||||
/// by the given number.
|
||||
#[inline]
|
||||
pub fn north(&self, z: $type) -> Self {
|
||||
Self {
|
||||
x: self.x,
|
||||
|
@ -80,6 +81,7 @@ macro_rules! vec3_impl {
|
|||
}
|
||||
/// Return a new instance of this position with the x coordinate increased
|
||||
/// by the given number.
|
||||
#[inline]
|
||||
pub fn east(&self, x: $type) -> Self {
|
||||
Self {
|
||||
x: self.x + x,
|
||||
|
@ -89,6 +91,7 @@ macro_rules! vec3_impl {
|
|||
}
|
||||
/// Return a new instance of this position with the z coordinate increased
|
||||
/// by the given number.
|
||||
#[inline]
|
||||
pub fn south(&self, z: $type) -> Self {
|
||||
Self {
|
||||
x: self.x,
|
||||
|
@ -98,6 +101,7 @@ macro_rules! vec3_impl {
|
|||
}
|
||||
/// Return a new instance of this position with the x coordinate subtracted
|
||||
/// by the given number.
|
||||
#[inline]
|
||||
pub fn west(&self, x: $type) -> Self {
|
||||
Self {
|
||||
x: self.x - x,
|
||||
|
@ -110,6 +114,16 @@ macro_rules! vec3_impl {
|
|||
pub fn dot(&self, other: Self) -> $type {
|
||||
self.x * other.x + self.y * other.y + self.z * other.z
|
||||
}
|
||||
|
||||
/// Replace the Y with 0.
|
||||
#[inline]
|
||||
pub fn xz(&self) -> Self {
|
||||
Self {
|
||||
x: self.x,
|
||||
y: <$type>::default(),
|
||||
z: self.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for &$name {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use azalea_block::BlockState;
|
||||
use azalea_buf::AzBuf;
|
||||
use azalea_core::position::BlockPos;
|
||||
use azalea_core::{color::RgbColor, position::BlockPos};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_registry::ParticleKind;
|
||||
use bevy_ecs::component::Component;
|
||||
|
@ -251,37 +252,21 @@ impl From<ParticleKind> for Particle {
|
|||
|
||||
#[derive(Debug, Clone, AzBuf, Default)]
|
||||
pub struct BlockParticle {
|
||||
#[var]
|
||||
pub block_state: i32,
|
||||
pub block_state: BlockState,
|
||||
}
|
||||
#[derive(Debug, Clone, AzBuf, Default)]
|
||||
pub struct DustParticle {
|
||||
/// Red value, 0-1
|
||||
pub red: f32,
|
||||
/// Green value, 0-1
|
||||
pub green: f32,
|
||||
/// Blue value, 0-1
|
||||
pub blue: f32,
|
||||
pub color: RgbColor,
|
||||
/// The scale, will be clamped between 0.01 and 4.
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AzBuf, Default)]
|
||||
pub struct DustColorTransitionParticle {
|
||||
/// Red value, 0-1
|
||||
pub from_red: f32,
|
||||
/// Green value, 0-1
|
||||
pub from_green: f32,
|
||||
/// Blue value, 0-1
|
||||
pub from_blue: f32,
|
||||
pub from: RgbColor,
|
||||
pub to: RgbColor,
|
||||
/// The scale, will be clamped between 0.01 and 4.
|
||||
pub scale: f32,
|
||||
/// Red value, 0-1
|
||||
pub to_red: f32,
|
||||
/// Green value, 0-1
|
||||
pub to_green: f32,
|
||||
/// Blue value, 0-1
|
||||
pub to_blue: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, AzBuf, Default)]
|
||||
|
|
|
@ -26,12 +26,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_c_level_particles_packet() {
|
||||
let slice = &[
|
||||
0, 0, 64, 36, 19, 1, 192, 139, 224, 69, 64, 91, 192, 0, 0, 0, 0, 0, 63, 229, 66, 62,
|
||||
20, 132, 232, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 25, 153, 154, 0, 0, 0, 70,
|
||||
1, 9,
|
||||
][..];
|
||||
let mut bytes = Cursor::new(slice);
|
||||
#[rustfmt::skip]
|
||||
let slice = [
|
||||
0, 0, 64, 156, 51, 153, 153, 153, 153, 154, 192, 64, 140, 204, 204, 204, 204, 205, 63, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 13, 255, 0, 255, 255, 63, 128, 0, 0
|
||||
];
|
||||
let mut bytes = Cursor::new(slice.as_slice());
|
||||
|
||||
let packet = ClientboundLevelParticles::azalea_read(&mut bytes).unwrap();
|
||||
println!("{packet:?}");
|
||||
|
|
|
@ -64,3 +64,7 @@ harness = false
|
|||
[[bench]]
|
||||
name = "physics"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "checks"
|
||||
harness = false
|
||||
|
|
36
azalea/benches/checks.rs
Normal file
36
azalea/benches/checks.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::hint::black_box;
|
||||
|
||||
use azalea::pathfinder::mining::MiningCache;
|
||||
pub use azalea_registry as registry;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn benchmark(c: &mut Criterion) {
|
||||
let mining_cache = MiningCache::new(None);
|
||||
|
||||
let stone = registry::Block::Stone.into();
|
||||
c.bench_function("is_liquid stone", |b| {
|
||||
b.iter(|| mining_cache.is_liquid(black_box(stone)));
|
||||
});
|
||||
|
||||
let water = registry::Block::Water.into();
|
||||
c.bench_function("is_liquid water", |b| {
|
||||
b.iter(|| mining_cache.is_liquid(black_box(water)));
|
||||
});
|
||||
|
||||
let lava = registry::Block::Lava.into();
|
||||
c.bench_function("is_liquid lava", |b| {
|
||||
b.iter(|| mining_cache.is_liquid(black_box(lava)));
|
||||
});
|
||||
|
||||
let waterlogged_slab = azalea_block::blocks::OakSlab {
|
||||
kind: azalea_block::properties::Type::Bottom,
|
||||
waterlogged: true,
|
||||
}
|
||||
.into();
|
||||
c.bench_function("is_liquid waterlogged slab", |b| {
|
||||
b.iter(|| mining_cache.is_liquid(black_box(waterlogged_slab)));
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, benchmark);
|
||||
criterion_main!(benches);
|
|
@ -2,7 +2,7 @@ use std::{hint::black_box, sync::Arc, time::Duration};
|
|||
|
||||
use azalea::{
|
||||
pathfinder::{
|
||||
astar::{self, a_star},
|
||||
astar::{self, a_star, PathfinderTimeout},
|
||||
goals::{BlockPosGoal, Goal},
|
||||
mining::MiningCache,
|
||||
world::CachedWorld,
|
||||
|
@ -139,7 +139,7 @@ fn run_pathfinder_benchmark(
|
|||
|n| goal.heuristic(n),
|
||||
successors,
|
||||
|n| goal.success(n),
|
||||
Duration::MAX,
|
||||
PathfinderTimeout::Time(Duration::MAX),
|
||||
);
|
||||
|
||||
assert!(!partial);
|
||||
|
|
|
@ -5,6 +5,7 @@ use azalea::{
|
|||
entity::{LookDirection, Position},
|
||||
interact::HitResultComponent,
|
||||
world::MinecraftEntityId,
|
||||
BlockPos,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
|
@ -102,4 +103,18 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
|
||||
1
|
||||
}));
|
||||
|
||||
commands.register(literal("getblock").then(argument("x", integer()).then(
|
||||
argument("y", integer()).then(argument("z", integer()).executes(|ctx: &Ctx| {
|
||||
let source = ctx.source.lock();
|
||||
let x = get_integer(ctx, "x").unwrap();
|
||||
let y = get_integer(ctx, "y").unwrap();
|
||||
let z = get_integer(ctx, "z").unwrap();
|
||||
println!("getblock xyz {x} {y} {z}");
|
||||
let block_pos = BlockPos::new(x, y, z);
|
||||
let block = source.bot.world().read().get_block_state(&block_pos);
|
||||
source.reply(&format!("Block at {block_pos:?} is {block:?}"));
|
||||
1
|
||||
})),
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
//! A relatively simple bot for demonstrating some of Azalea's capabilities.
|
||||
//!
|
||||
//! Usage:
|
||||
//! ## Usage
|
||||
//!
|
||||
//! - Modify the consts below if necessary.
|
||||
//! - Run `cargo r --example testbot -- --owner <owner> --name <username/email>
|
||||
//! --address <address>`.
|
||||
//! - Run `cargo r --example testbot -- [arguments]`. (see below)
|
||||
//! - Commands are prefixed with `!` in chat. You can send them either in public
|
||||
//! chat or as a /msg.
|
||||
//! - Some commands to try are `!goto`, `!killaura true`, `!down`. Check the
|
||||
//! `commands` directory to see all of them.
|
||||
//!
|
||||
//! ### Arguments
|
||||
//!
|
||||
//! - `--owner` or `-O`: The username of the player who owns the bot. The bot
|
||||
//! will ignore commands from other players.
|
||||
//! - `--name` or `-N`: The username or email of the bot.
|
||||
//! - `--address` or `-A`: The address of the server to join.
|
||||
//! - `--pathfinder-debug-particles` or `-P`: Whether the bot should run
|
||||
//! /particle a ton of times to show where it's pathfinding to. You should
|
||||
//! only have this on if the bot has operator permissions, otherwise it'll
|
||||
//! just spam the server console unnecessarily.
|
||||
|
||||
#![feature(async_closure)]
|
||||
#![feature(trivial_bounds)]
|
||||
|
@ -28,11 +39,6 @@ use azalea::ClientInformation;
|
|||
use commands::{register_commands, CommandSource};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
/// Whether the bot should run /particle a ton of times to show where it's
|
||||
/// pathfinding to. You should only have this on if the bot has operator
|
||||
/// permissions, otherwise it'll just spam the server console unnecessarily.
|
||||
const PATHFINDER_DEBUG_PARTICLES: bool = false;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = parse_args();
|
||||
|
@ -121,7 +127,7 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> anyhow::Resu
|
|||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
if PATHFINDER_DEBUG_PARTICLES {
|
||||
if state.args.pathfinder_debug_particles {
|
||||
bot.ecs
|
||||
.lock()
|
||||
.entity_mut(bot.entity)
|
||||
|
@ -208,12 +214,14 @@ pub struct Args {
|
|||
pub owner: String,
|
||||
pub name: String,
|
||||
pub address: String,
|
||||
pub pathfinder_debug_particles: bool,
|
||||
}
|
||||
|
||||
fn parse_args() -> Args {
|
||||
let mut owner_username = None;
|
||||
let mut bot_username = None;
|
||||
let mut address = None;
|
||||
let mut pathfinder_debug_particles = false;
|
||||
|
||||
let mut args = env::args().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
|
@ -227,6 +235,9 @@ fn parse_args() -> Args {
|
|||
"--address" | "-A" => {
|
||||
address = args.next();
|
||||
}
|
||||
"--pathfinder-debug-particles" | "-P" => {
|
||||
pathfinder_debug_particles = true;
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Unknown argument: {}", arg);
|
||||
process::exit(1);
|
||||
|
@ -238,5 +249,6 @@ fn parse_args() -> Args {
|
|||
owner: owner_username.unwrap_or_else(|| "admin".to_string()),
|
||||
name: bot_username.unwrap_or_else(|| "azalea".to_string()),
|
||||
address: address.unwrap_or_else(|| "localhost".to_string()),
|
||||
pathfinder_debug_particles,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{cell::UnsafeCell, ops::RangeInclusive};
|
||||
|
||||
use azalea_block::{BlockState, BlockStates};
|
||||
use azalea_block::{properties::Waterlogged, BlockState, BlockStates};
|
||||
use azalea_core::bitset::BitSet;
|
||||
use azalea_inventory::Menu;
|
||||
use nohash_hasher::IntMap;
|
||||
|
||||
|
@ -96,8 +97,12 @@ impl MiningCache {
|
|||
}
|
||||
|
||||
pub fn is_liquid(&self, block: BlockState) -> bool {
|
||||
// this already runs in about 1 nanosecond, so if you wanna try optimizing it at
|
||||
// least run the benchmarks (in benches/checks.rs)
|
||||
|
||||
self.water_block_state_range.contains(&block.id)
|
||||
|| self.lava_block_state_range.contains(&block.id)
|
||||
|| is_waterlogged(block)
|
||||
}
|
||||
|
||||
pub fn is_falling_block(&self, block: BlockState) -> bool {
|
||||
|
@ -106,3 +111,7 @@ impl MiningCache {
|
|||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_waterlogged(block: BlockState) -> bool {
|
||||
block.property::<Waterlogged>().unwrap_or_default()
|
||||
}
|
||||
|
|
|
@ -594,7 +594,7 @@ pub fn check_node_reached(
|
|||
executing_path.path = executing_path.path.split_off(i + 1);
|
||||
executing_path.last_reached_node = movement.target;
|
||||
executing_path.last_node_reached_at = Instant::now();
|
||||
trace!("reached node {:?}", movement.target);
|
||||
trace!("reached node {}", movement.target);
|
||||
|
||||
if let Some(new_path) = executing_path.queued_path.take() {
|
||||
debug!(
|
||||
|
@ -696,7 +696,7 @@ pub fn recalculate_near_end_of_path(
|
|||
&& executing_path.is_path_partial
|
||||
{
|
||||
if let Some(goal) = pathfinder.goal.as_ref().cloned() {
|
||||
debug!("Recalculating path because it ends soon");
|
||||
debug!("Recalculating path because it's empty or ends soon");
|
||||
debug!(
|
||||
"recalculate_near_end_of_path executing_path.is_path_partial: {}",
|
||||
executing_path.is_path_partial
|
||||
|
@ -953,7 +953,7 @@ mod tests {
|
|||
goal: Arc::new(BlockPosGoal(end_pos)),
|
||||
successors_fn: moves::default_move,
|
||||
allow_mining: false,
|
||||
deterministic_timeout: false,
|
||||
deterministic_timeout: true,
|
||||
});
|
||||
simulation
|
||||
}
|
||||
|
@ -1161,7 +1161,7 @@ mod tests {
|
|||
let mut simulation = setup_blockposgoal_simulation(
|
||||
&mut partial_chunks,
|
||||
BlockPos::new(0, 71, 0),
|
||||
BlockPos::new(2, 74, 9),
|
||||
BlockPos::new(4, 74, 9),
|
||||
vec![
|
||||
BlockPos::new(0, 70, 0),
|
||||
BlockPos::new(0, 70, 1),
|
||||
|
@ -1169,9 +1169,11 @@ mod tests {
|
|||
BlockPos::new(0, 71, 3),
|
||||
BlockPos::new(0, 72, 6),
|
||||
BlockPos::new(0, 73, 9),
|
||||
// this is the point where the bot might fall if it has too much momentum
|
||||
BlockPos::new(2, 73, 9),
|
||||
BlockPos::new(4, 73, 9),
|
||||
],
|
||||
);
|
||||
assert_simulation_reaches(&mut simulation, 80, BlockPos::new(2, 74, 9));
|
||||
assert_simulation_reaches(&mut simulation, 80, BlockPos::new(4, 74, 9));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue