mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
pathfinding works*
This commit is contained in:
parent
6d1b58b868
commit
9f046adf9b
4 changed files with 193 additions and 50 deletions
|
@ -42,31 +42,36 @@ impl Trait for azalea_client::Client {
|
|||
fn goto(&self, goal: impl Goal) {
|
||||
let start = Node {
|
||||
pos: BlockPos::from(self.entity().pos()),
|
||||
vertical_vel: VerticalVel::None,
|
||||
};
|
||||
let end = goal.goal_node();
|
||||
println!("start: {:?}, end: {:?}", start, end);
|
||||
|
||||
let successors = |node: &Node| {
|
||||
println!("successors for {:?}", node);
|
||||
let mut edges = Vec::new();
|
||||
let possible_moves: Vec<&dyn moves::Move> = vec![
|
||||
&moves::NorthMove,
|
||||
&moves::SouthMove,
|
||||
&moves::EastMove,
|
||||
&moves::WestMove,
|
||||
&moves::JumpUpMove,
|
||||
&moves::FallNorthMove,
|
||||
&moves::FallSouthMove,
|
||||
&moves::FallEastMove,
|
||||
&moves::FallWestMove,
|
||||
&moves::LandMove,
|
||||
];
|
||||
let dimension = self.dimension.read();
|
||||
for possible_move in possible_moves.iter() {
|
||||
let can_execute = possible_move.can_execute(&dimension, &node.pos);
|
||||
edges.push(Edge {
|
||||
target: Node {
|
||||
pos: node.pos + possible_move.offset(),
|
||||
target: possible_move.next_node(&node),
|
||||
cost: if possible_move.can_execute(&dimension, node) {
|
||||
possible_move.cost()
|
||||
} else {
|
||||
f32::INFINITY
|
||||
},
|
||||
cost: if can_execute { 1.0 } else { f32::INFINITY },
|
||||
});
|
||||
println!("can execute for {:?}: {}", node, can_execute);
|
||||
}
|
||||
println!("edges: {}", edges.len());
|
||||
edges
|
||||
};
|
||||
|
||||
|
@ -79,6 +84,7 @@ impl Trait for azalea_client::Client {
|
|||
|n| goal.success(n),
|
||||
);
|
||||
let p = pf.find_path();
|
||||
println!("path: {:?}", p);
|
||||
|
||||
let state = self.plugins.get::<Plugin>().unwrap().state.clone();
|
||||
// convert the Option<Vec<Node>> to a VecDeque<Node>
|
||||
|
@ -93,22 +99,39 @@ fn tick_execute_path(bot: &mut Client, path: &mut VecDeque<Node>) {
|
|||
return;
|
||||
};
|
||||
let center = target.pos.center();
|
||||
println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
|
||||
// println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
|
||||
bot.look_at(¢er);
|
||||
bot.walk(WalkDirection::Forward);
|
||||
// check if we should jump
|
||||
if target.pos.y > bot.entity().pos().y.floor() as i32 {
|
||||
bot.jump();
|
||||
}
|
||||
|
||||
if target.is_reached(&bot.entity()) {
|
||||
println!("ok target reached");
|
||||
println!("ok target {target:?} reached");
|
||||
path.pop_front();
|
||||
if path.is_empty() {
|
||||
bot.walk(WalkDirection::None);
|
||||
}
|
||||
// tick again, maybe we already reached the next node!
|
||||
tick_execute_path(bot, path);
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about our vertical velocity
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
|
||||
pub enum VerticalVel {
|
||||
None,
|
||||
/// No vertical velocity, but we're not on the ground
|
||||
NoneMidair,
|
||||
// less than 3 blocks (no fall damage)
|
||||
FallingLittle,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
|
||||
pub struct Node {
|
||||
pub pos: BlockPos,
|
||||
pub vertical_vel: VerticalVel,
|
||||
}
|
||||
|
||||
pub trait Goal {
|
||||
|
@ -123,13 +146,19 @@ impl Node {
|
|||
/// Returns whether the entity is at the node and should start going to the
|
||||
/// next node.
|
||||
pub fn is_reached(&self, entity: &EntityData) -> bool {
|
||||
// println!(
|
||||
// "entity.yya: {} {:?}=={:?}",
|
||||
// entity.yya,
|
||||
// BlockPos::from(entity.pos()),
|
||||
// self.pos
|
||||
// );
|
||||
entity.yya == 0. && BlockPos::from(entity.pos()) == self.pos
|
||||
println!(
|
||||
"entity.delta.y: {} {:?}=={:?}, self.vertical_vel={:?}",
|
||||
entity.delta.y,
|
||||
BlockPos::from(entity.pos()),
|
||||
self.pos,
|
||||
self.vertical_vel
|
||||
);
|
||||
BlockPos::from(entity.pos()) == self.pos
|
||||
&& match self.vertical_vel {
|
||||
VerticalVel::NoneMidair => (entity.delta.y > -0.1 && entity.delta.y < 0.1),
|
||||
VerticalVel::None => entity.on_ground,
|
||||
VerticalVel::FallingLittle => entity.delta.y < -0.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +176,10 @@ impl Goal for BlockPosGoal {
|
|||
n.pos == self.pos
|
||||
}
|
||||
fn goal_node(&self) -> Node {
|
||||
Node { pos: self.pos }
|
||||
Node {
|
||||
pos: self.pos,
|
||||
vertical_vel: VerticalVel::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,11 @@ use azalea_core::BlockPos;
|
|||
use azalea_physics::collision::{self, BlockWithShape};
|
||||
use azalea_world::Dimension;
|
||||
|
||||
use crate::{Node, VerticalVel};
|
||||
|
||||
/// whether this block is passable
|
||||
fn is_passable(pos: &BlockPos, dim: &Dimension) -> bool {
|
||||
if let Some(block) = dim.get_block_state(pos) {
|
||||
println!(
|
||||
"is passable {pos:?} {} = {}",
|
||||
Box::<dyn Block>::from(block).id(),
|
||||
block.shape() == &collision::empty_shape()
|
||||
);
|
||||
block.shape() == &collision::empty_shape()
|
||||
} else {
|
||||
false
|
||||
|
@ -20,11 +17,6 @@ fn is_passable(pos: &BlockPos, dim: &Dimension) -> bool {
|
|||
/// whether this block has a solid hitbox (i.e. we can stand on it)
|
||||
fn is_solid(pos: &BlockPos, dim: &Dimension) -> bool {
|
||||
if let Some(block) = dim.get_block_state(pos) {
|
||||
println!(
|
||||
"is solid {pos:?} {} = {}",
|
||||
Box::<dyn Block>::from(block).id(),
|
||||
block.shape() == &collision::block_shape()
|
||||
);
|
||||
block.shape() == &collision::block_shape()
|
||||
} else {
|
||||
false
|
||||
|
@ -38,15 +30,26 @@ fn is_standable(pos: &BlockPos, dim: &Dimension) -> bool {
|
|||
}
|
||||
|
||||
pub trait Move {
|
||||
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool;
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool;
|
||||
/// Returns by how much the entity's position should be changed when this move is executed.
|
||||
fn offset(&self) -> BlockPos;
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::None,
|
||||
}
|
||||
}
|
||||
fn cost(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NorthMove;
|
||||
impl Move for NorthMove {
|
||||
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
|
||||
is_standable(&(pos + &self.offset()), dim)
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_standable(&(node.pos + self.offset()), dim)
|
||||
&& (node.vertical_vel == VerticalVel::None
|
||||
|| node.vertical_vel == VerticalVel::NoneMidair)
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, 0, -1)
|
||||
|
@ -55,8 +58,10 @@ impl Move for NorthMove {
|
|||
|
||||
pub struct SouthMove;
|
||||
impl Move for SouthMove {
|
||||
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
|
||||
is_standable(&(pos + &self.offset()), dim)
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_standable(&(node.pos + self.offset()), dim)
|
||||
&& (node.vertical_vel == VerticalVel::None
|
||||
|| node.vertical_vel == VerticalVel::NoneMidair)
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, 0, 1)
|
||||
|
@ -65,8 +70,10 @@ impl Move for SouthMove {
|
|||
|
||||
pub struct EastMove;
|
||||
impl Move for EastMove {
|
||||
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
|
||||
is_standable(&(pos + &self.offset()), dim)
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_standable(&(node.pos + self.offset()), dim)
|
||||
&& (node.vertical_vel == VerticalVel::None
|
||||
|| node.vertical_vel == VerticalVel::NoneMidair)
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(1, 0, 0)
|
||||
|
@ -75,14 +82,126 @@ impl Move for EastMove {
|
|||
|
||||
pub struct WestMove;
|
||||
impl Move for WestMove {
|
||||
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
|
||||
is_standable(&(pos + &self.offset()), dim)
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_standable(&(node.pos + self.offset()), dim)
|
||||
&& (node.vertical_vel == VerticalVel::None
|
||||
|| node.vertical_vel == VerticalVel::NoneMidair)
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(-1, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JumpUpMove;
|
||||
impl Move for JumpUpMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_passable(&node.pos.up(2), dim) && node.vertical_vel == VerticalVel::None
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, 1, 0)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::NoneMidair,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct FallNorthMove;
|
||||
impl Move for FallNorthMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
// check whether 3 blocks vertically forward are passable
|
||||
is_passable(&(node.pos + self.offset().down(1)), dim)
|
||||
&& is_passable(&(node.pos + self.offset()), dim)
|
||||
&& is_passable(&(node.pos + self.offset().up(1)), dim)
|
||||
&& node.vertical_vel == VerticalVel::None
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, 0, -1)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::NoneMidair,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct FallSouthMove;
|
||||
impl Move for FallSouthMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
// check whether 3 blocks vertically forward are passable
|
||||
is_passable(&(node.pos + self.offset().down(1)), dim)
|
||||
&& is_passable(&(node.pos + self.offset()), dim)
|
||||
&& is_passable(&(node.pos + self.offset().up(1)), dim)
|
||||
&& node.vertical_vel == VerticalVel::None
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, 0, 1)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::NoneMidair,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct FallEastMove;
|
||||
impl Move for FallEastMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
// check whether 3 blocks vertically forward are passable
|
||||
is_passable(&(node.pos + self.offset().down(1)), dim)
|
||||
&& is_passable(&(node.pos + self.offset()), dim)
|
||||
&& is_passable(&(node.pos + self.offset().up(1)), dim)
|
||||
&& node.vertical_vel == VerticalVel::None
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(1, 0, 0)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::NoneMidair,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct FallWestMove;
|
||||
impl Move for FallWestMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
// check whether 3 blocks vertically forward are passable
|
||||
is_passable(&(node.pos + self.offset().down(1)), dim)
|
||||
&& is_passable(&(node.pos + self.offset()), dim)
|
||||
&& is_passable(&(node.pos + self.offset().up(1)), dim)
|
||||
&& node.vertical_vel == VerticalVel::None
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(-1, 0, 0)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::NoneMidair,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LandMove;
|
||||
impl Move for LandMove {
|
||||
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
|
||||
is_standable(&(node.pos + self.offset()), dim)
|
||||
&& (node.vertical_vel == VerticalVel::NoneMidair
|
||||
|| node.vertical_vel == VerticalVel::FallingLittle)
|
||||
}
|
||||
fn offset(&self) -> BlockPos {
|
||||
BlockPos::new(0, -1, 0)
|
||||
}
|
||||
fn next_node(&self, node: &Node) -> Node {
|
||||
Node {
|
||||
pos: node.pos + self.offset(),
|
||||
vertical_vel: VerticalVel::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -15,21 +15,14 @@ pub struct State {
|
|||
}
|
||||
|
||||
pub trait BotTrait {
|
||||
fn jump(&self);
|
||||
fn jump(&mut self);
|
||||
fn look_at(&mut self, pos: &Vec3);
|
||||
}
|
||||
|
||||
impl BotTrait for azalea_client::Client {
|
||||
/// Queue a jump for the next tick.
|
||||
fn jump(&self) {
|
||||
{
|
||||
let player_entity_id = self.player.read().entity_id;
|
||||
let mut dimension_lock = self.dimension.write();
|
||||
let mut player_entity = dimension_lock
|
||||
.entity_mut(player_entity_id)
|
||||
.expect("Player must exist");
|
||||
player_entity.jumping = true;
|
||||
}
|
||||
fn jump(&mut self) {
|
||||
self.set_jumping(true);
|
||||
let state = self.plugins.get::<Plugin>().unwrap().state.clone();
|
||||
*state.jumping_once.lock() = true;
|
||||
}
|
||||
|
@ -48,8 +41,7 @@ impl crate::Plugin for Plugin {
|
|||
if *self.state.jumping_once.lock() {
|
||||
if bot.jumping() {
|
||||
*self.state.jumping_once.lock() = false;
|
||||
} else {
|
||||
bot.set_jumping(true);
|
||||
bot.set_jumping(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
|||
.pos()
|
||||
.clone();
|
||||
let target_pos: BlockPos = (&target_pos_vec3).into();
|
||||
bot.look_at(&target_pos_vec3);
|
||||
// bot.goto(BlockPosGoal::from(target_pos));
|
||||
// bot.look_at(&target_pos_vec3);
|
||||
bot.goto(BlockPosGoal::from(target_pos));
|
||||
// bot.walk(WalkDirection::Forward);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue