feat(client): add inventory manipulation wrappers
This commit is contained in:
parent
2373d6500e
commit
21cc1a5488
19
lib/enum.lua
19
lib/enum.lua
@ -13,3 +13,22 @@ RADIUS_GOAL = 1
|
||||
REACH_BLOCK_POS_GOAL = 2
|
||||
XZ_GOAL = 3
|
||||
Y_GOAL = 4
|
||||
|
||||
PICKUP_LEFT = 0
|
||||
PICKUP_RIGHT = 1
|
||||
PICKUP_LEFT_OUTSIDE = 2
|
||||
PICKUP_RIGHT_OUTSIDE = 3
|
||||
QUICK_MOVE_LEFT = 4
|
||||
QUICK_MOVE_RIGHT = 5
|
||||
SWAP = 6
|
||||
CLONE = 7
|
||||
THROW_SINGLE = 8
|
||||
THROW_ALL = 9
|
||||
QUICK_CRAFT = 10
|
||||
QUICK_CRAFT_LEFT = 0
|
||||
QUICK_CRAFT_RIGHT = 1
|
||||
QUICK_CRAFT_MIDDLE = 2
|
||||
QUICK_CRAFT_START = 0
|
||||
QUICK_CRAFT_ADD = 1
|
||||
QUICK_CRAFT_END = 2
|
||||
PICKUP_ALL = 11
|
||||
|
88
src/lua/client/container.rs
Normal file
88
src/lua/client/container.rs
Normal 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(())
|
||||
}
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
50
src/lua/container/item_stack.rs
Normal file
50
src/lua/container/item_stack.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use azalea::inventory::components::{CustomName, Damage, MaxDamage};
|
||||
use mlua::{UserData, UserDataFields, UserDataMethods};
|
||||
|
||||
pub struct ItemStack {
|
||||
pub inner: azalea::inventory::ItemStack,
|
||||
}
|
||||
|
||||
impl UserData for ItemStack {
|
||||
fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
|
||||
f.add_field_method_get("is_empty", |_, this| Ok(this.inner.is_empty()));
|
||||
f.add_field_method_get("is_present", |_, this| Ok(this.inner.is_present()));
|
||||
f.add_field_method_get("count", |_, this| Ok(this.inner.count()));
|
||||
f.add_field_method_get("kind", |_, this| Ok(this.inner.kind().to_string()));
|
||||
f.add_field_method_get("custom_name", |_, this| {
|
||||
Ok(if let Some(data) = this.inner.as_present() {
|
||||
data.components
|
||||
.get::<CustomName>()
|
||||
.map(|n| n.name.to_string())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
f.add_field_method_get("damage", |_, this| {
|
||||
Ok(if let Some(data) = this.inner.as_present() {
|
||||
data.components.get::<Damage>().map(|d| d.amount)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
f.add_field_method_get("max_damage", |_, this| {
|
||||
Ok(if let Some(data) = this.inner.as_present() {
|
||||
data.components.get::<MaxDamage>().map(|d| d.amount)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
|
||||
m.add_method_mut("split", |_, this, count: u32| {
|
||||
Ok(ItemStack {
|
||||
inner: this.inner.split(count),
|
||||
})
|
||||
});
|
||||
m.add_method_mut("update_empty", |_, this, (): ()| {
|
||||
this.inner.update_empty();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
124
src/lua/container/mod.rs
Normal file
124
src/lua/container/mod.rs
Normal file
@ -0,0 +1,124 @@
|
||||
pub mod item_stack;
|
||||
|
||||
use azalea::{
|
||||
container::{ContainerHandle, ContainerHandleRef},
|
||||
inventory::operations::{
|
||||
ClickOperation, CloneClick, PickupAllClick, PickupClick, QuickCraftClick, QuickCraftKind,
|
||||
QuickCraftStatus, QuickMoveClick, SwapClick, ThrowClick,
|
||||
},
|
||||
};
|
||||
use item_stack::ItemStack;
|
||||
use mlua::{Result, Table, UserData, UserDataFields, UserDataMethods};
|
||||
|
||||
pub struct Container {
|
||||
pub inner: ContainerHandle,
|
||||
}
|
||||
|
||||
impl UserData for Container {
|
||||
fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
|
||||
f.add_field_method_get("id", |_, this| Ok(this.inner.id()));
|
||||
|
||||
f.add_field_method_get("menu", |_, this| {
|
||||
Ok(this.inner.menu().map(|m| format!("{m:?}")))
|
||||
});
|
||||
|
||||
f.add_field_method_get("contents", |_, this| {
|
||||
Ok(this.inner.contents().map(|v| {
|
||||
v.iter()
|
||||
.map(|i| ItemStack { inner: i.clone() })
|
||||
.collect::<Vec<_>>()
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContainerRef {
|
||||
pub inner: ContainerHandleRef,
|
||||
}
|
||||
|
||||
impl UserData for ContainerRef {
|
||||
fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
|
||||
f.add_field_method_get("id", |_, this| Ok(this.inner.id()));
|
||||
|
||||
f.add_field_method_get("menu", |_, this| {
|
||||
Ok(this.inner.menu().map(|m| format!("{m:?}")))
|
||||
});
|
||||
|
||||
f.add_field_method_get("contents", |_, this| {
|
||||
Ok(this.inner.contents().map(|v| {
|
||||
v.iter()
|
||||
.map(|i| ItemStack { inner: i.clone() })
|
||||
.collect::<Vec<_>>()
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
|
||||
m.add_method("close", |_, this, (): ()| {
|
||||
this.inner.close();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
m.add_method("click", |_, this, operation: Table| {
|
||||
this.inner.click(click_operation_from_table(operation)?);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn click_operation_from_table(o: Table) -> Result<ClickOperation> {
|
||||
Ok(match o.get("type")? {
|
||||
0 => ClickOperation::Pickup(PickupClick::Left {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
1 => ClickOperation::Pickup(PickupClick::Right {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
2 => ClickOperation::Pickup(PickupClick::LeftOutside),
|
||||
3 => ClickOperation::Pickup(PickupClick::RightOutside),
|
||||
5 => ClickOperation::QuickMove(QuickMoveClick::Right {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
6 => ClickOperation::Swap(SwapClick {
|
||||
source_slot: o.get("source_slot")?,
|
||||
target_slot: o.get("target_slot")?,
|
||||
}),
|
||||
7 => ClickOperation::Clone(CloneClick {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
8 => ClickOperation::Throw(ThrowClick::Single {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
9 => ClickOperation::Throw(ThrowClick::All {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
10 => ClickOperation::QuickCraft(QuickCraftClick {
|
||||
kind: match o.get("kind").unwrap_or_default() {
|
||||
1 => QuickCraftKind::Right,
|
||||
2 => QuickCraftKind::Middle,
|
||||
_ => QuickCraftKind::Left,
|
||||
},
|
||||
status: match o.get("status").unwrap_or_default() {
|
||||
1 => QuickCraftStatus::Add {
|
||||
slot: o.get("slot")?,
|
||||
},
|
||||
2 => QuickCraftStatus::End,
|
||||
_ => QuickCraftStatus::Start,
|
||||
},
|
||||
}),
|
||||
11 => ClickOperation::PickupAll(PickupAllClick {
|
||||
slot: o.get("slot")?,
|
||||
reversed: o.get("reversed").unwrap_or_default(),
|
||||
}),
|
||||
_ => ClickOperation::QuickMove(QuickMoveClick::Left {
|
||||
slot: o.get("slot")?,
|
||||
}),
|
||||
})
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod block;
|
||||
pub mod client;
|
||||
pub mod container;
|
||||
pub mod direction;
|
||||
pub mod entity;
|
||||
pub mod fluid_state;
|
||||
|
Loading…
x
Reference in New Issue
Block a user