refactor: random fixes and usage improvements

This commit is contained in:
Ryan 2025-02-17 17:01:23 -05:00
parent 8f2fbf11da
commit dde489a8ed
Signed by: ErrorNoInternet
GPG Key ID: 2486BFB7B1E6A4A3
15 changed files with 203 additions and 90 deletions

View File

@ -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"

View File

@ -6,6 +6,8 @@ for _, module in
{
"enum",
"events",
"inventory",
"movement",
"utils",
}
do

View File

@ -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

38
lib/inventory.lua Normal file
View File

@ -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

18
lib/movement.lua Normal file
View File

@ -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

View File

@ -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 = "{ "

View File

@ -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 {

View File

@ -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(

View File

@ -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<Option<Block>> {
let Ok(state) = BlockState::try_from(state) else {
return Ok(None);
};
let block: Box<dyn AzaleaBlock> = 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<String>, Option<Function>),
) -> Result<Vec<u16>> {
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::<Box<dyn AzaleaBlock>>::into(b).id()
&& (if let Some(filter_fn) = &filter_fn {
let p = lua.create_table()?;
p.set("chest_type", b.property::<ChestType>().map(|v| v as u8))?;
p.set("facing", b.property::<Facing>().map(|v| v as u8))?;
p.set("light_level", b.property::<LightLevel>().map(|v| v as u8))?;
filter_fn.call::<bool>(p.clone())?
} else {
true
})
{
matched.push(b.id);
}
}
}
Ok(matched)
}

View File

@ -6,6 +6,15 @@ use azalea::{
use log::error;
use mlua::{Lua, Result, UserDataRef};
pub fn container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> {
Ok(client
.inner
.as_ref()
.unwrap()
.get_open_container()
.map(|c| ContainerRef { inner: c }))
}
pub fn held_item(_lua: &Lua, client: &Client) -> Result<ItemStack> {
Ok(ItemStack {
inner: client
@ -26,15 +35,6 @@ pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> {
.selected_hotbar_slot)
}
pub fn open_container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> {
Ok(client
.inner
.as_ref()
.unwrap()
.get_open_container()
.map(|c| ContainerRef { inner: c }))
}
pub async fn open_container_at(
_lua: Lua,
client: UserDataRef<Client>,

View File

@ -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: UserDataFields<Self>>(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: UserDataMethods<Self>>(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);

View File

@ -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<Direction> {
let d = client.inner.as_ref().unwrap().direction();
@ -23,9 +23,9 @@ pub fn eye_position(_lua: &Lua, client: &Client) -> Result<Vec3> {
})
}
pub fn goto(
lua: &Lua,
client: &mut Client,
pub async fn goto(
lua: Lua,
client: UserDataRef<Client>,
(data, metadata): (Value, Option<Table>),
) -> 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::<GotoEvent>(client.entity).is_none() {
break;
}
}
Ok(())
}

View File

@ -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<String>,
) -> Result<Vec<u16>> {
Ok(block_names
.iter()
.flat_map(|n| {
(u32::MIN..u32::MAX)
.map_while(|i| BlockState::try_from(i).ok())
.filter(move |&b| n == Into::<Box<dyn AzaleaBlock>>::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::<bool>(entity.clone()).unwrap() {
if filter_fn.call::<bool>(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<Option<Block>> {
let Ok(state) = BlockState::try_from(state) else {
return Ok(None);
};
let block: Box<dyn AzaleaBlock> = 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<Option<u16>> {
#[allow(clippy::cast_possible_truncation)]
Ok(client

View File

@ -32,10 +32,14 @@ impl UserData for Container {
}
fn add_methods<M: UserDataMethods<Self>>(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<u8>)| {
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<u8>)| {
this.inner
.click(click_operation_from_table(operation, operation_type)?);
Ok(())
},
);
}
}
fn click_operation_from_table(o: Table) -> Result<ClickOperation> {
Ok(match o.get("type")? {
fn click_operation_from_table(op: Table, op_type: Option<u8>) -> Result<ClickOperation> {
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")?,
}),
})
}

View File

@ -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> {