use azalea::{ BlockPos, BotClientExt, SprintDirection, WalkDirection, core::hit_result::HitResult, entity::Position, interact::HitResultComponent, pathfinder::{ ExecutingPath, Pathfinder, PathfinderClientExt, goals::{BlockPosGoal, Goal, InverseGoal, RadiusGoal, ReachBlockPosGoal, XZGoal, YGoal}, }, protocol::packets::game::{ServerboundPlayerCommand, s_player_command::Action}, world::MinecraftEntityId, }; use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value}; use super::{Client, Direction, Vec3}; #[derive(Debug)] struct AnyGoal(Box); impl Goal for AnyGoal { fn success(&self, n: BlockPos) -> bool { self.0.success(n) } fn heuristic(&self, n: BlockPos) -> f32 { self.0.heuristic(n) } } #[allow(clippy::cast_possible_truncation)] fn to_goal(lua: &Lua, client: &Client, data: Table, options: &Table, kind: u8) -> Result { let goal: Box = match kind { 1 => { let pos = Vec3::from_lua(data.get("position")?, lua)?; Box::new(RadiusGoal { pos: azalea::Vec3::new(pos.x, pos.y, pos.z), radius: data.get("radius")?, }) } 2 => { let distance = data.get("distance").unwrap_or(4.5); let pos = Vec3::from_lua(Value::Table(data), lua)?; Box::new(ReachBlockPosGoal::new_with_distance( BlockPos::new(pos.x as i32, pos.y as i32, pos.z as i32), distance, client.world().read().chunks.clone(), )) } 3 => Box::new(XZGoal { x: data.get("x")?, z: data.get("z")?, }), 4 => Box::new(YGoal { y: data.get("y")? }), _ => { let pos = Vec3::from_lua(Value::Table(data), lua)?; Box::new(BlockPosGoal(BlockPos::new( pos.x as i32, pos.y as i32, pos.z as i32, ))) } }; Ok(AnyGoal(if options.get("inverse").unwrap_or_default() { Box::new(InverseGoal(AnyGoal(goal))) } else { goal })) } pub fn go_to_reached(_lua: &Lua, client: &Client) -> Result { Ok(client.is_goto_target_reached()) } pub async fn wait_until_goal_reached(_lua: Lua, client: UserDataRef, (): ()) -> Result<()> { client.wait_until_goto_target_reached().await; Ok(()) } pub async fn go_to( lua: Lua, client: UserDataRef, (data, metadata): (Table, Option), ) -> Result<()> { let metadata = metadata.unwrap_or(lua.create_table()?); let options = metadata.get("options").unwrap_or(lua.create_table()?); let goal = to_goal( &lua, &client, data, &options, metadata.get("type").unwrap_or_default(), )?; if options.get("without_mining").unwrap_or_default() { client.start_goto_without_mining(goal); client.wait_until_goto_target_reached().await; } else { client.goto(goal).await; } Ok(()) } pub async fn start_go_to( lua: Lua, client: UserDataRef, (data, metadata): (Table, Option
), ) -> Result<()> { let metadata = metadata.unwrap_or(lua.create_table()?); let options = metadata.get("options").unwrap_or(lua.create_table()?); let goal = to_goal( &lua, &client, data, &options, metadata.get("type").unwrap_or_default(), )?; if options.get("without_mining").unwrap_or_default() { client.start_goto_without_mining(goal); } else { client.start_goto(goal); } let _ = client.get_tick_broadcaster().recv().await; Ok(()) } pub fn direction(_lua: &Lua, client: &Client) -> Result { let direction = client.direction(); Ok(Direction { y: direction.0, x: direction.1, }) } pub fn eye_position(_lua: &Lua, client: &Client) -> Result { Ok(Vec3::from(client.eye_position())) } pub fn jump(_lua: &Lua, client: &Client, (): ()) -> Result<()> { client.jump(); Ok(()) } pub fn looking_at(lua: &Lua, client: &Client) -> Result> { Ok( if let HitResult::Block(ref result) = *client.component::() { let table = lua.create_table()?; table.set("direction", Vec3::from(result.direction.normal()))?; table.set("inside", result.inside)?; table.set("location", Vec3::from(result.location))?; table.set("position", Vec3::from(result.block_pos))?; table.set("world_border", result.world_border)?; Some(table) } else { None }, ) } pub fn look_at(_lua: &Lua, client: &Client, position: Vec3) -> Result<()> { client.look_at(azalea::Vec3::new(position.x, position.y, position.z)); Ok(()) } pub fn pathfinder(lua: &Lua, client: &Client) -> Result
{ let table = lua.create_table()?; table.set( "is_calculating", client.component::().is_calculating, )?; table.set( "is_executing", if let Some(pathfinder) = client.get_component::() { table.set( "last_reached_node", Vec3::from(pathfinder.last_reached_node), )?; table.set( "last_node_reach_elapsed", pathfinder.last_node_reached_at.elapsed().as_millis(), )?; table.set("is_path_partial", pathfinder.is_path_partial)?; true } else { false }, )?; Ok(table) } pub fn position(_lua: &Lua, client: &Client) -> Result { Ok(Vec3::from(&client.component::())) } pub fn set_direction(_lua: &Lua, client: &Client, direction: Direction) -> Result<()> { client.set_direction(direction.y, direction.x); Ok(()) } pub fn set_jumping(_lua: &Lua, client: &Client, jumping: bool) -> Result<()> { client.set_jumping(jumping); Ok(()) } pub fn set_position(_lua: &Lua, client: &Client, new_position: Vec3) -> Result<()> { let mut ecs = client.ecs.lock(); let mut position = client.query::<&mut Position>(&mut ecs); position.x = new_position.x; position.y = new_position.y; position.z = new_position.z; Ok(()) } pub fn set_sneaking(_lua: &Lua, client: &Client, sneaking: bool) -> Result<()> { client.write_packet(ServerboundPlayerCommand { id: client.component::(), action: if sneaking { Action::PressShiftKey } else { Action::ReleaseShiftKey }, data: 0, }); Ok(()) } pub fn sprint(_lua: &Lua, client: &Client, direction: u8) -> Result<()> { client.sprint(match direction { 5 => SprintDirection::ForwardRight, 6 => SprintDirection::ForwardLeft, _ => SprintDirection::Forward, }); Ok(()) } pub fn stop_pathfinding(_lua: &Lua, client: &Client, (): ()) -> Result<()> { client.stop_pathfinding(); Ok(()) } pub fn stop_sleeping(_lua: &Lua, client: &Client, (): ()) -> Result<()> { client.write_packet(ServerboundPlayerCommand { id: client.component::(), action: Action::StopSleeping, data: 0, }); Ok(()) } pub fn walk(_lua: &Lua, client: &Client, direction: u8) -> Result<()> { client.walk(match direction { 1 => WalkDirection::Forward, 2 => WalkDirection::Backward, 3 => WalkDirection::Left, 4 => WalkDirection::Right, 5 => WalkDirection::ForwardRight, 6 => WalkDirection::ForwardLeft, 7 => WalkDirection::BackwardRight, 8 => WalkDirection::BackwardLeft, _ => WalkDirection::None, }); Ok(()) }