feat(client): add inventory manipulation wrappers

This commit is contained in:
2025-02-17 13:38:20 -05:00
parent 2373d6500e
commit 21cc1a5488
7 changed files with 331 additions and 11 deletions

View File

@@ -0,0 +1,88 @@
use super::{Client, Container, ContainerRef, ItemStack, Vec3};
use azalea::{
BlockPos, inventory::Inventory, prelude::ContainerClientExt,
protocol::packets::game::ServerboundSetCarriedItem,
};
use log::error;
use mlua::{Lua, Result, UserDataRef};
pub fn held_item(_lua: &Lua, client: &Client) -> Result<ItemStack> {
Ok(ItemStack {
inner: client
.inner
.as_ref()
.unwrap()
.component::<Inventory>()
.held_item(),
})
}
pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> {
Ok(client
.inner
.as_ref()
.unwrap()
.component::<Inventory>()
.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>,
position: Vec3,
) -> Result<Option<Container>> {
#[allow(clippy::cast_possible_truncation)]
Ok(client
.inner
.clone()
.unwrap()
.open_container_at(BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
))
.await
.map(|c| Container { inner: c }))
}
pub fn open_inventory(_lua: &Lua, client: &mut Client, _: ()) -> Result<Option<Container>> {
Ok(client
.inner
.as_mut()
.unwrap()
.open_inventory()
.map(|c| Container { inner: c }))
}
pub fn set_held_slot(_lua: &Lua, client: &Client, slot: u8) -> Result<()> {
if slot > 8 {
return Ok(());
}
let client = client.inner.as_ref().unwrap();
{
let mut ecs = client.ecs.lock();
let mut inventory = client.query::<&mut Inventory>(&mut ecs);
if inventory.selected_hotbar_slot == slot {
return Ok(());
}
inventory.selected_hotbar_slot = slot;
};
if let Err(error) = client.write_packet(ServerboundSetCarriedItem {
slot: u16::from(slot),
}) {
error!("failed to send SetCarriedItem packet: {error:?}");
}
Ok(())
}

View File

@@ -1,10 +1,17 @@
mod container;
mod interaction;
mod movement;
mod state;
mod world;
use super::{
block::Block, direction::Direction, entity::Entity, fluid_state::FluidState, hunger::Hunger,
block::Block,
container::item_stack::ItemStack,
container::{Container, ContainerRef},
direction::Direction,
entity::Entity,
fluid_state::FluidState,
hunger::Hunger,
vec3::Vec3,
};
use azalea::Client as AzaleaClient;
@@ -20,8 +27,11 @@ impl UserData for Client {
f.add_field_method_get("direction", movement::direction);
f.add_field_method_get("eye_position", movement::eye_position);
f.add_field_method_get("health", state::health);
f.add_field_method_get("held_item", container::held_item);
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);
@@ -31,13 +41,17 @@ impl UserData for Client {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
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("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);
m.add_method("set_mining", interaction::set_mining);
m.add_method("stop_pathfinding", movement::stop_pathfinding);
m.add_method_mut("attack", interaction::attack);
@@ -45,6 +59,7 @@ impl UserData for Client {
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);
m.add_method_mut("set_direction", movement::set_direction);
m.add_method_mut("set_jumping", movement::set_jumping);
m.add_method_mut("sprint", movement::sprint);

View File

@@ -1,17 +1,47 @@
use super::{Block, Client, Entity, FluidState, Vec3};
use azalea::{
BlockPos,
auto_tool::AutoToolClientExt,
blocks::{Block as AzaleaBlock, BlockState, BlockStates},
ecs::query::Without,
entity::{Dead, EntityKind, EntityUuid, Position as AzaleaPosition, metadata::CustomName},
world::MinecraftEntityId,
};
use mlua::{Function, Lua, Result};
use mlua::{Function, Lua, Result, Table};
pub fn best_tool_for_block(lua: &Lua, client: &Client, block_state: u16) -> Result<Table> {
let tr = client
.inner
.as_ref()
.unwrap()
.best_tool_in_hotbar_for_block(BlockState { id: block_state });
let tool_result = lua.create_table()?;
tool_result.set("index", tr.index)?;
tool_result.set("percentage_per_tick", tr.percentage_per_tick)?;
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,
(nearest_to, block_names): (Vec3, Vec<String>),
(nearest_to, block_states): (Vec3, Vec<u16>),
) -> Result<Vec<Vec3>> {
#[allow(clippy::cast_possible_truncation)]
Ok(client
@@ -27,14 +57,7 @@ pub fn find_blocks(
nearest_to.z as i32,
),
&BlockStates {
set: 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())
})
.collect(),
set: block_states.iter().map(|&id| BlockState { id }).collect(),
},
)
.map(|p| Vec3 {