diff --git a/Cargo.toml b/Cargo.toml index e17c9d7..12e2746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,17 +12,16 @@ opt-level = 3 [profile.release] codegen-units = 1 lto = true -panic = "abort" strip = true [dependencies] anyhow = "1" azalea = { git = "https://github.com/azalea-rs/azalea.git" } clap = { version = "4", features = ["derive"] } +futures = "0" http-body-util = "0" hyper = { version = "1", features = ["server"] } hyper-util = "0" log = { version = "0" } mlua = { version = "0", features = ["async", "luau", "send"] } tokio = { version = "1", features = ["macros"] } -futures = "0" diff --git a/errornowatcher.lua b/errornowatcher.lua index fc641ba..c55f286 100644 --- a/errornowatcher.lua +++ b/errornowatcher.lua @@ -6,6 +6,8 @@ for _, module in { "enum", "events", + "inventory", + "movement", "utils", } do diff --git a/lib/events.lua b/lib/events.lua index a7f4c1f..8a960c4 100644 --- a/lib/events.lua +++ b/lib/events.lua @@ -1,5 +1,5 @@ local center = { x = 0, z = 0 } -local radius = 50 +local radius = 100 function on_tick() local entities = client:find_entities(function(e) @@ -15,6 +15,10 @@ function on_tick() end function on_init() - info("client initialized, setting client information") + info("client initialized, setting information...") client:set_client_information({ view_distance = 16 }) end + +function on_login() + info("player successfully logged in!") +end diff --git a/lib/inventory.lua b/lib/inventory.lua new file mode 100644 index 0000000..d99e618 --- /dev/null +++ b/lib/inventory.lua @@ -0,0 +1,38 @@ +function steal(item_name) + for _, chest_pos in client:find_blocks(client.position, get_block_states({ "chest" })) do + client:chat(dump(chest_pos)) + + client:goto({ position = chest_pos, radius = 3 }, { type = RADIUS_GOAL }) + while client.pathfinder.is_calculating or client.pathfinder.is_executing do + sleep(50) + end + client:look_at(chest_pos) + + local container = client:open_container_at(chest_pos) + for index, item in container.contents do + if item.kind == item_name then + container:click({slot = index - 1}, THROW_ALL) + sleep(50) + end + end + + container = nil + while client.open_container do + sleep(50) + end + end +end + +function drop_all_hotbar() + local inventory = client:open_inventory() + for i = 0, 9 do + inventory:click({slot = 36 + i}, THROW_ALL) + end +end + +function drop_all_inventory() + local inventory = client:open_inventory() + for i = 0, 45 do + inventory:click({slot = i}, THROW_ALL) + end +end diff --git a/lib/movement.lua b/lib/movement.lua new file mode 100644 index 0000000..e885d92 --- /dev/null +++ b/lib/movement.lua @@ -0,0 +1,18 @@ +function look_at_player(name) + local player = get_player(name) + if player then + player.position.y = player.position.y + 1 + client:look_at(player.position) + else + client:chat("player not found!") + end +end + +function goto_player(name) + local player = get_player(name) + if player then + client:goto(player.position) + else + client:chat("player not found!") + end +end diff --git a/lib/utils.lua b/lib/utils.lua index 4ef5ba8..cf6023b 100644 --- a/lib/utils.lua +++ b/lib/utils.lua @@ -1,3 +1,17 @@ +function get_player(name) + local target_uuid = nil + for uuid, player in client.tab_list do + if player.name == name then + target_uuid = uuid + break + end + end + + return client:find_entities(function(e) + return e.kind == "minecraft:player" and e.uuid == target_uuid + end)[1] +end + function dump(object) if type(object) == "table" then local string = "{ " diff --git a/src/events.rs b/src/events.rs index 9569634..fec8620 100644 --- a/src/events.rs +++ b/src/events.rs @@ -7,6 +7,7 @@ use mlua::{Function, IntoLuaMulti, Table}; use tokio::net::TcpListener; pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow::Result<()> { + state.lua.gc_stop(); let globals = state.lua.globals(); match event { diff --git a/src/http.rs b/src/http.rs index acd8d97..3ac8bf5 100644 --- a/src/http.rs +++ b/src/http.rs @@ -24,8 +24,8 @@ pub async fn serve( let bytes = request.into_body().collect().await?.to_bytes(); match std::str::from_utf8(&bytes) { Ok(code) => Response::new(full(match path.as_str() { - "/eval" => format!("{:?}", eval(&state.lua, code).await), - "/exec" => format!("{:?}", exec(&state.lua, code).await), + "/eval" => format!("{:#?}", eval(&state.lua, code).await), + "/exec" => format!("{:#?}", exec(&state.lua, code).await), _ => unreachable!(), })), Err(error) => status_code_response( diff --git a/src/lua/block.rs b/src/lua/block.rs index 25c8cd0..747b442 100644 --- a/src/lua/block.rs +++ b/src/lua/block.rs @@ -1,4 +1,8 @@ -use mlua::{FromLua, IntoLua, Lua, Result, Value}; +use azalea::blocks::{ + Block as AzaleaBlock, BlockState, + properties::{ChestType, Facing, LightLevel}, +}; +use mlua::{FromLua, Function, IntoLua, Lua, Result, Table, Value}; #[derive(Clone)] pub struct Block { @@ -46,3 +50,57 @@ impl FromLua for Block { } } } + +pub fn register_functions(lua: &Lua, globals: &Table) -> Result<()> { + globals.set( + "get_block_from_state", + lua.create_function(get_block_from_state)?, + )?; + globals.set("get_block_states", lua.create_function(get_block_states)?)?; + + Ok(()) +} + +pub fn get_block_from_state(_lua: &Lua, state: u32) -> Result> { + let Ok(state) = BlockState::try_from(state) else { + return Ok(None); + }; + let block: Box = state.into(); + let behavior = block.behavior(); + + Ok(Some(Block { + id: block.id().to_string(), + friction: behavior.friction, + jump_factor: behavior.jump_factor, + destroy_time: behavior.destroy_time, + explosion_resistance: behavior.explosion_resistance, + requires_correct_tool_for_drops: behavior.requires_correct_tool_for_drops, + })) +} + +pub fn get_block_states( + lua: &Lua, + (block_names, filter_fn): (Vec, Option), +) -> Result> { + let mut matched = Vec::new(); + for block_name in block_names { + for b in + (u32::MIN..u32::MAX).map_while(|possible_id| BlockState::try_from(possible_id).ok()) + { + if block_name == Into::>::into(b).id() + && (if let Some(filter_fn) = &filter_fn { + let p = lua.create_table()?; + p.set("chest_type", b.property::().map(|v| v as u8))?; + p.set("facing", b.property::().map(|v| v as u8))?; + p.set("light_level", b.property::().map(|v| v as u8))?; + filter_fn.call::(p.clone())? + } else { + true + }) + { + matched.push(b.id); + } + } + } + Ok(matched) +} diff --git a/src/lua/client/container.rs b/src/lua/client/container.rs index d0d72ac..81d166b 100644 --- a/src/lua/client/container.rs +++ b/src/lua/client/container.rs @@ -6,6 +6,15 @@ use azalea::{ use log::error; use mlua::{Lua, Result, UserDataRef}; +pub fn container(_lua: &Lua, client: &Client) -> Result> { + Ok(client + .inner + .as_ref() + .unwrap() + .get_open_container() + .map(|c| ContainerRef { inner: c })) +} + pub fn held_item(_lua: &Lua, client: &Client) -> Result { Ok(ItemStack { inner: client @@ -26,15 +35,6 @@ pub fn held_slot(_lua: &Lua, client: &Client) -> Result { .selected_hotbar_slot) } -pub fn open_container(_lua: &Lua, client: &Client) -> Result> { - Ok(client - .inner - .as_ref() - .unwrap() - .get_open_container() - .map(|c| ContainerRef { inner: c })) -} - pub async fn open_container_at( _lua: Lua, client: UserDataRef, diff --git a/src/lua/client/mod.rs b/src/lua/client/mod.rs index 6b60d7a..bf25489 100644 --- a/src/lua/client/mod.rs +++ b/src/lua/client/mod.rs @@ -5,7 +5,6 @@ mod state; mod world; use super::{ - block::Block, container::item_stack::ItemStack, container::{Container, ContainerRef}, direction::Direction, @@ -24,6 +23,7 @@ pub struct Client { impl UserData for Client { fn add_fields>(f: &mut F) { f.add_field_method_get("air_supply", state::air_supply); + f.add_field_method_get("container", container::container); f.add_field_method_get("direction", movement::direction); f.add_field_method_get("eye_position", movement::eye_position); f.add_field_method_get("has_attack_cooldown", interaction::has_attack_cooldown); @@ -32,7 +32,6 @@ impl UserData for Client { f.add_field_method_get("held_slot", container::held_slot); f.add_field_method_get("hunger", state::hunger); f.add_field_method_get("looking_at", movement::looking_at); - f.add_field_method_get("open_container", container::open_container); f.add_field_method_get("pathfinder", movement::pathfinder); f.add_field_method_get("position", movement::position); f.add_field_method_get("score", state::score); @@ -41,16 +40,15 @@ impl UserData for Client { } fn add_methods>(m: &mut M) { + m.add_async_method("goto", movement::goto); m.add_async_method("mine", interaction::mine); m.add_async_method("open_container_at", container::open_container_at); m.add_async_method("set_client_information", state::set_client_information); m.add_method("best_tool_for_block", world::best_tool_for_block); - m.add_method("block_names_to_states", world::block_names_to_states); m.add_method("chat", chat); m.add_method("disconnect", disconnect); m.add_method("find_blocks", world::find_blocks); m.add_method("find_entities", world::find_entities); - m.add_method("get_block_from_state", world::get_block_from_state); m.add_method("get_block_state", world::get_block_state); m.add_method("get_fluid_state", world::get_fluid_state); m.add_method("set_held_slot", container::set_held_slot); @@ -58,7 +56,6 @@ impl UserData for Client { m.add_method("stop_pathfinding", movement::stop_pathfinding); m.add_method_mut("attack", interaction::attack); m.add_method_mut("block_interact", interaction::block_interact); - m.add_method_mut("goto", movement::goto); m.add_method_mut("jump", movement::jump); m.add_method_mut("look_at", movement::look_at); m.add_method_mut("open_inventory", container::open_inventory); diff --git a/src/lua/client/movement.rs b/src/lua/client/movement.rs index 6025add..5658912 100644 --- a/src/lua/client/movement.rs +++ b/src/lua/client/movement.rs @@ -3,11 +3,11 @@ use azalea::{ BlockPos, BotClientExt, Client as AzaleaClient, SprintDirection, WalkDirection, interact::HitResultComponent, pathfinder::{ - ExecutingPath, Pathfinder, PathfinderClientExt, + ExecutingPath, GotoEvent, Pathfinder, PathfinderClientExt, goals::{BlockPosGoal, Goal, RadiusGoal, ReachBlockPosGoal, XZGoal, YGoal}, }, }; -use mlua::{FromLua, Lua, Result, Table, Value}; +use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value}; pub fn direction(_lua: &Lua, client: &Client) -> Result { let d = client.inner.as_ref().unwrap().direction(); @@ -23,9 +23,9 @@ pub fn eye_position(_lua: &Lua, client: &Client) -> Result { }) } -pub fn goto( - lua: &Lua, - client: &mut Client, +pub async fn goto( + lua: Lua, + client: UserDataRef, (data, metadata): (Value, Option), ) -> Result<()> { fn g(client: &AzaleaClient, without_mining: bool, goal: impl Goal + Send + Sync + 'static) { @@ -55,7 +55,7 @@ pub fn goto( match goal_type { 1 => { let t = data.as_table().ok_or(error)?; - let p = Vec3::from_lua(t.get("position")?, lua)?; + let p = Vec3::from_lua(t.get("position")?, &lua)?; g( client, without_mining, @@ -66,7 +66,7 @@ pub fn goto( ); } 2 => { - let p = Vec3::from_lua(data, lua)?; + let p = Vec3::from_lua(data, &lua)?; g( client, without_mining, @@ -95,7 +95,7 @@ pub fn goto( }, ), _ => { - let p = Vec3::from_lua(data, lua)?; + let p = Vec3::from_lua(data, &lua)?; g( client, without_mining, @@ -104,6 +104,12 @@ pub fn goto( } } + while client.get_tick_broadcaster().recv().await.is_ok() { + if client.ecs.lock().get::(client.entity).is_none() { + break; + } + } + Ok(()) } diff --git a/src/lua/client/world.rs b/src/lua/client/world.rs index 5c92814..2049cb4 100644 --- a/src/lua/client/world.rs +++ b/src/lua/client/world.rs @@ -1,8 +1,8 @@ -use super::{Block, Client, Entity, FluidState, Vec3}; +use super::{Client, Entity, FluidState, Vec3}; use azalea::{ BlockPos, auto_tool::AutoToolClientExt, - blocks::{Block as AzaleaBlock, BlockState, BlockStates}, + blocks::{BlockState, BlockStates}, ecs::query::Without, entity::{Dead, EntityKind, EntityUuid, Position as AzaleaPosition, metadata::CustomName}, world::MinecraftEntityId, @@ -22,22 +22,6 @@ pub fn best_tool_for_block(lua: &Lua, client: &Client, block_state: u16) -> Resu Ok(tool_result) } -pub fn block_names_to_states( - _lua: &Lua, - _client: &Client, - block_names: Vec, -) -> Result> { - Ok(block_names - .iter() - .flat_map(|n| { - (u32::MIN..u32::MAX) - .map_while(|i| BlockState::try_from(i).ok()) - .filter(move |&b| n == Into::>::into(b).id()) - .map(|b| b.id) - }) - .collect()) -} - pub fn find_blocks( _lua: &Lua, client: &Client, @@ -93,7 +77,7 @@ pub fn find_entities(_lua: &Lua, client: &Client, filter_fn: Function) -> Result custom_name: custom_name.as_ref().map(ToString::to_string), }; - if filter_fn.call::(entity.clone()).unwrap() { + if filter_fn.call::(entity.clone())? { matched.push(entity); } } @@ -101,23 +85,6 @@ pub fn find_entities(_lua: &Lua, client: &Client, filter_fn: Function) -> Result Ok(matched) } -pub fn get_block_from_state(_lua: &Lua, _client: &Client, state: u32) -> Result> { - let Ok(state) = BlockState::try_from(state) else { - return Ok(None); - }; - let block: Box = state.into(); - let behavior = block.behavior(); - - Ok(Some(Block { - id: block.id().to_string(), - friction: behavior.friction, - jump_factor: behavior.jump_factor, - destroy_time: behavior.destroy_time, - explosion_resistance: behavior.explosion_resistance, - requires_correct_tool_for_drops: behavior.requires_correct_tool_for_drops, - })) -} - pub fn get_block_state(_lua: &Lua, client: &Client, position: Vec3) -> Result> { #[allow(clippy::cast_possible_truncation)] Ok(client diff --git a/src/lua/container/mod.rs b/src/lua/container/mod.rs index a71076f..8063982 100644 --- a/src/lua/container/mod.rs +++ b/src/lua/container/mod.rs @@ -32,10 +32,14 @@ impl UserData for Container { } fn add_methods>(m: &mut M) { - m.add_method("click", |_, this, operation: Table| { - this.inner.click(click_operation_from_table(operation)?); - Ok(()) - }); + m.add_method( + "click", + |_, this, (operation, operation_type): (Table, Option)| { + this.inner + .click(click_operation_from_table(operation, operation_type)?); + Ok(()) + }, + ); } } @@ -66,59 +70,63 @@ impl UserData for ContainerRef { Ok(()) }); - m.add_method("click", |_, this, operation: Table| { - this.inner.click(click_operation_from_table(operation)?); - Ok(()) - }); + m.add_method( + "click", + |_, this, (operation, operation_type): (Table, Option)| { + this.inner + .click(click_operation_from_table(operation, operation_type)?); + Ok(()) + }, + ); } } -fn click_operation_from_table(o: Table) -> Result { - Ok(match o.get("type")? { +fn click_operation_from_table(op: Table, op_type: Option) -> Result { + Ok(match op_type.unwrap_or_default() { 0 => ClickOperation::Pickup(PickupClick::Left { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 1 => ClickOperation::Pickup(PickupClick::Right { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 2 => ClickOperation::Pickup(PickupClick::LeftOutside), 3 => ClickOperation::Pickup(PickupClick::RightOutside), 5 => ClickOperation::QuickMove(QuickMoveClick::Right { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 6 => ClickOperation::Swap(SwapClick { - source_slot: o.get("source_slot")?, - target_slot: o.get("target_slot")?, + source_slot: op.get("source_slot")?, + target_slot: op.get("target_slot")?, }), 7 => ClickOperation::Clone(CloneClick { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 8 => ClickOperation::Throw(ThrowClick::Single { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 9 => ClickOperation::Throw(ThrowClick::All { - slot: o.get("slot")?, + slot: op.get("slot")?, }), 10 => ClickOperation::QuickCraft(QuickCraftClick { - kind: match o.get("kind").unwrap_or_default() { + kind: match op.get("kind").unwrap_or_default() { 1 => QuickCraftKind::Right, 2 => QuickCraftKind::Middle, _ => QuickCraftKind::Left, }, - status: match o.get("status").unwrap_or_default() { + status: match op.get("status").unwrap_or_default() { 1 => QuickCraftStatus::Add { - slot: o.get("slot")?, + slot: op.get("slot")?, }, 2 => QuickCraftStatus::End, _ => QuickCraftStatus::Start, }, }), 11 => ClickOperation::PickupAll(PickupAllClick { - slot: o.get("slot")?, - reversed: o.get("reversed").unwrap_or_default(), + slot: op.get("slot")?, + reversed: op.get("reversed").unwrap_or_default(), }), _ => ClickOperation::QuickMove(QuickMoveClick::Left { - slot: o.get("slot")?, + slot: op.get("slot")?, }), }) } diff --git a/src/lua/mod.rs b/src/lua/mod.rs index 7e1fb77..827ec15 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -29,7 +29,8 @@ pub fn register_functions(lua: &Lua, globals: &Table) -> mlua::Result<()> { })?, )?; - logging::register_functions(lua, globals) + logging::register_functions(lua, globals)?; + block::register_functions(lua, globals) } pub fn reload(lua: &Lua) -> Result<(), Error> {