aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2024-12-24 08:48:36 +0000
committermat <git@matdoes.dev>2024-12-24 08:48:36 +0000
commitf03e0c22355778a9863cccb5a59d852278d60701 (patch)
treef02e7ca3d1e975d486071934a6322d372b7c9a02
parentde5a53ce08de5b9d77bce99dd9ecde3171ebd74e (diff)
downloadazalea-drasl-f03e0c22355778a9863cccb5a59d852278d60701.tar.xz
fix parsing Dust particle and treat waterlogged blocks as liquid in pathfinder
-rwxr-xr-xazalea-block/azalea-block-macros/src/lib.rs7
-rwxr-xr-xazalea-block/src/lib.rs6
-rwxr-xr-xazalea-core/src/bitset.rs4
-rw-r--r--azalea-core/src/color.rs55
-rwxr-xr-xazalea-core/src/lib.rs1
-rwxr-xr-xazalea-core/src/position.rs14
-rwxr-xr-xazalea-entity/src/particle.rs27
-rwxr-xr-xazalea-protocol/src/packets/game/c_level_particles.rs11
-rw-r--r--azalea/Cargo.toml4
-rw-r--r--azalea/benches/checks.rs36
-rw-r--r--azalea/benches/pathfinder.rs4
-rw-r--r--azalea/examples/testbot/commands/debug.rs15
-rw-r--r--azalea/examples/testbot/main.rs30
-rw-r--r--azalea/src/pathfinder/mining.rs11
-rw-r--r--azalea/src/pathfinder/mod.rs12
15 files changed, 183 insertions, 54 deletions
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs
index 742d1fc9..de13ff6e 100755
--- a/azalea-block/azalea-block-macros/src/lib.rs
+++ b/azalea-block/azalea-block-macros/src/lib.rs
@@ -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.
///
diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs
index c1655919..3de0aa5a 100755
--- a/azalea-block/src/lib.rs
+++ b/azalea-block/src/lib.rs
@@ -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]
diff --git a/azalea-core/src/bitset.rs b/azalea-core/src/bitset.rs
index 76b04885..5af05e19 100755
--- a/azalea-core/src/bitset.rs
+++ b/azalea-core/src/bitset.rs
@@ -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)],
}
}
diff --git a/azalea-core/src/color.rs b/azalea-core/src/color.rs
new file mode 100644
index 00000000..eddf5035
--- /dev/null
+++ b/azalea-core/src/color.rs
@@ -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
+ }
+}
diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs
index acfb560e..04422146 100755
--- a/azalea-core/src/lib.rs
+++ b/azalea-core/src/lib.rs
@@ -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;
diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs
index be06e825..ab73c0d1 100755
--- a/azalea-core/src/position.rs
+++ b/azalea-core/src/position.rs
@@ -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 {
diff --git a/azalea-entity/src/particle.rs b/azalea-entity/src/particle.rs
index 76559e58..a8710eff 100755
--- a/azalea-entity/src/particle.rs
+++ b/azalea-entity/src/particle.rs
@@ -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)]
diff --git a/azalea-protocol/src/packets/game/c_level_particles.rs b/azalea-protocol/src/packets/game/c_level_particles.rs
index 4f77af84..d54315ab 100755
--- a/azalea-protocol/src/packets/game/c_level_particles.rs
+++ b/azalea-protocol/src/packets/game/c_level_particles.rs
@@ -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:?}");
diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml
index ab11ccb3..59f253bd 100644
--- a/azalea/Cargo.toml
+++ b/azalea/Cargo.toml
@@ -64,3 +64,7 @@ harness = false
[[bench]]
name = "physics"
harness = false
+
+[[bench]]
+name = "checks"
+harness = false
diff --git a/azalea/benches/checks.rs b/azalea/benches/checks.rs
new file mode 100644
index 00000000..b246f1f6
--- /dev/null
+++ b/azalea/benches/checks.rs
@@ -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);
diff --git a/azalea/benches/pathfinder.rs b/azalea/benches/pathfinder.rs
index 4e42c63a..bd47a3bf 100644
--- a/azalea/benches/pathfinder.rs
+++ b/azalea/benches/pathfinder.rs
@@ -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);
diff --git a/azalea/examples/testbot/commands/debug.rs b/azalea/examples/testbot/commands/debug.rs
index ae0808cb..ab323b2f 100644
--- a/azalea/examples/testbot/commands/debug.rs
+++ b/azalea/examples/testbot/commands/debug.rs
@@ -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
+ })),
+ )));
}
diff --git a/azalea/examples/testbot/main.rs b/azalea/examples/testbot/main.rs
index 81cd2bb8..3d9f999e 100644
--- a/azalea/examples/testbot/main.rs
+++ b/azalea/examples/testbot/main.rs
@@ -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,
}
}
diff --git a/azalea/src/pathfinder/mining.rs b/azalea/src/pathfinder/mining.rs
index 31b37b49..049e974a 100644
--- a/azalea/src/pathfinder/mining.rs
+++ b/azalea/src/pathfinder/mining.rs
@@ -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()
+}
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index b82b5f84..8ad0b1d0 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -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));
}
}