fix(client): handle borrows with async

This commit is contained in:
2026-04-13 13:58:58 -04:00
parent d6c16f0d5d
commit 57397d3718
7 changed files with 84 additions and 50 deletions

View File

@@ -7,6 +7,7 @@ use azalea::{
use mlua::{Lua, Result, UserDataRef, Value}; use mlua::{Lua, Result, UserDataRef, Value};
use super::{Client, Container, ContainerRef, ItemStack, Vec3}; use super::{Client, Container, ContainerRef, ItemStack, Vec3};
use crate::unpack;
pub fn container(_lua: &Lua, client: &Client) -> Result<ContainerRef> { pub fn container(_lua: &Lua, client: &Client) -> Result<ContainerRef> {
Ok(ContainerRef(client.get_inventory())) Ok(ContainerRef(client.get_inventory()))
@@ -95,9 +96,10 @@ pub async fn open_container_at(
client: UserDataRef<Client>, client: UserDataRef<Client>,
position: Vec3, position: Vec3,
) -> Result<Option<Container>> { ) -> Result<Option<Container>> {
let client = unpack!(client);
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok(client Ok(client
.clone()
.open_container_at(BlockPos::new( .open_container_at(BlockPos::new(
position.x as i32, position.x as i32,
position.y as i32, position.y as i32,

View File

@@ -6,6 +6,7 @@ use azalea::{
use mlua::{Lua, Result, UserDataRef}; use mlua::{Lua, Result, UserDataRef};
use super::{Client, Vec3}; use super::{Client, Vec3};
use crate::unpack;
pub fn attack(_lua: &Lua, client: &Client, entity_id: i32) -> Result<()> { pub fn attack(_lua: &Lua, client: &Client, entity_id: i32) -> Result<()> {
if let Some(entity) = client.entity_id_by_minecraft_id(MinecraftEntityId(entity_id)) { if let Some(entity) = client.entity_id_by_minecraft_id(MinecraftEntityId(entity_id)) {
@@ -29,9 +30,10 @@ pub fn has_attack_cooldown(_lua: &Lua, client: &Client) -> Result<bool> {
} }
pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Result<()> { pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Result<()> {
let client = unpack!(client);
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
client client
.clone()
.mine(BlockPos::new( .mine(BlockPos::new(
position.x as i32, position.x as i32,
position.y as i32, position.y as i32,

View File

@@ -6,7 +6,7 @@ mod movement;
mod state; mod state;
mod world; mod world;
use std::ops::{Deref, DerefMut}; use std::ops::Deref;
use azalea::{Client as AzaleaClient, core::entity_id::MinecraftEntityId}; use azalea::{Client as AzaleaClient, core::entity_id::MinecraftEntityId};
use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods}; use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods};
@@ -28,12 +28,6 @@ impl Deref for Client {
} }
} }
impl DerefMut for Client {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().expect("should have received init event")
}
}
impl UserData for Client { impl UserData for Client {
fn add_fields<F: UserDataFields<Self>>(f: &mut F) { fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("air_supply", state::air_supply); f.add_field_method_get("air_supply", state::air_supply);
@@ -121,3 +115,12 @@ fn username(_lua: &Lua, client: &Client) -> Result<String> {
fn uuid(_lua: &Lua, client: &Client) -> Result<String> { fn uuid(_lua: &Lua, client: &Client) -> Result<String> {
Ok(client.uuid().to_string()) Ok(client.uuid().to_string())
} }
#[macro_export]
macro_rules! unpack {
($client:ident) => {{
let inner = (**$client).clone();
drop($client);
inner
}};
}

View File

@@ -1,5 +1,5 @@
use azalea::{ use azalea::{
BlockPos, SprintDirection, WalkDirection, BlockPos, Client as AzaleaClient, SprintDirection, WalkDirection,
core::{entity_id::MinecraftEntityId, hit_result::HitResult}, core::{entity_id::MinecraftEntityId, hit_result::HitResult},
entity::Position, entity::Position,
interact::pick::HitResultComponent, interact::pick::HitResultComponent,
@@ -12,6 +12,7 @@ use azalea::{
use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value}; use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value};
use super::{Client, Direction, Vec3}; use super::{Client, Direction, Vec3};
use crate::unpack;
#[derive(Debug)] #[derive(Debug)]
struct AnyGoal(Box<dyn Goal>); struct AnyGoal(Box<dyn Goal>);
@@ -27,7 +28,7 @@ impl Goal for AnyGoal {
} }
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn to_goal(lua: &Lua, client: &Client, data: Table, kind: u8) -> Result<AnyGoal> { fn to_goal(lua: &Lua, client: &AzaleaClient, data: Table, kind: u8) -> Result<AnyGoal> {
let goal: Box<dyn Goal> = match kind { let goal: Box<dyn Goal> = match kind {
1 => { 1 => {
let pos = Vec3::from_lua(data.get("position")?, lua)?; let pos = Vec3::from_lua(data.get("position")?, lua)?;
@@ -68,6 +69,7 @@ pub fn go_to_reached(_lua: &Lua, client: &Client) -> Result<bool> {
} }
pub async fn wait_until_goal_reached(_lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<()> { pub async fn wait_until_goal_reached(_lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<()> {
let client = unpack!(client);
client.wait_until_goto_target_reached().await; client.wait_until_goto_target_reached().await;
Ok(()) Ok(())
} }
@@ -77,6 +79,8 @@ pub async fn go_to(
client: UserDataRef<Client>, client: UserDataRef<Client>,
(data, metadata): (Table, Option<Table>), (data, metadata): (Table, Option<Table>),
) -> Result<()> { ) -> Result<()> {
let client = unpack!(client);
let metadata = metadata.unwrap_or(lua.create_table()?); let metadata = metadata.unwrap_or(lua.create_table()?);
let options = metadata.get("options").unwrap_or(lua.create_table()?); let options = metadata.get("options").unwrap_or(lua.create_table()?);
let goal = to_goal( let goal = to_goal(
@@ -85,12 +89,13 @@ pub async fn go_to(
data, data,
metadata.get("type").unwrap_or_default(), metadata.get("type").unwrap_or_default(),
)?; )?;
if options.get("without_mining").unwrap_or_default() { client
client.start_goto_with_opts(goal, PathfinderOpts::new().allow_mining(false)); .goto_with_opts(
client.wait_until_goto_target_reached().await; goal,
} else { PathfinderOpts::new().allow_mining(options.get("without_mining").unwrap_or_default()),
client.goto(goal).await; )
} .await;
Ok(()) Ok(())
} }
@@ -99,6 +104,8 @@ pub async fn start_go_to(
client: UserDataRef<Client>, client: UserDataRef<Client>,
(data, metadata): (Table, Option<Table>), (data, metadata): (Table, Option<Table>),
) -> Result<()> { ) -> Result<()> {
let client = unpack!(client);
let metadata = metadata.unwrap_or(lua.create_table()?); let metadata = metadata.unwrap_or(lua.create_table()?);
let options = metadata.get("options").unwrap_or(lua.create_table()?); let options = metadata.get("options").unwrap_or(lua.create_table()?);
let goal = to_goal( let goal = to_goal(

View File

@@ -6,6 +6,7 @@ use azalea_hax::AntiKnockback;
use mlua::{Error, Lua, Result, Table, UserDataRef}; use mlua::{Error, Lua, Result, Table, UserDataRef};
use super::Client; use super::Client;
use crate::unpack;
pub fn air_supply(_lua: &Lua, client: &Client) -> Result<i32> { pub fn air_supply(_lua: &Lua, client: &Client) -> Result<i32> {
Ok(client.component::<AirSupply>().0) Ok(client.component::<AirSupply>().0)
@@ -37,6 +38,8 @@ pub async fn set_client_information(
client: UserDataRef<Client>, client: UserDataRef<Client>,
info: Table, info: Table,
) -> Result<()> { ) -> Result<()> {
let client = unpack!(client);
let get_bool = |table: &Table, name| table.get(name).unwrap_or(true); let get_bool = |table: &Table, name| table.get(name).unwrap_or(true);
client.set_client_information(ClientInformation { client.set_client_information(ClientInformation {
allows_listing: info.get("allows_listing")?, allows_listing: info.get("allows_listing")?,

View File

@@ -10,7 +10,7 @@ use azalea::{
use mlua::{Function, Lua, Result, Table, UserDataRef}; use mlua::{Function, Lua, Result, Table, UserDataRef};
use super::{Client, Direction, Vec3}; use super::{Client, Direction, Vec3};
use crate::lua::client::MinecraftEntityId; use crate::{lua::client::MinecraftEntityId, unpack};
pub fn blocks( pub fn blocks(
_lua: &Lua, _lua: &Lua,
@@ -39,6 +39,8 @@ pub fn blocks(
} }
pub async fn all_entities(lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<Vec<Table>> { pub async fn all_entities(lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<Vec<Table>> {
let client = unpack!(client);
let mut matched = Vec::with_capacity(256); let mut matched = Vec::with_capacity(256);
for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in
get_entities!(client) get_entities!(client)
@@ -65,6 +67,8 @@ pub async fn entities(
client: UserDataRef<Client>, client: UserDataRef<Client>,
filter_fn: Function, filter_fn: Function,
) -> Result<Vec<Table>> { ) -> Result<Vec<Table>> {
let client = unpack!(client);
let mut matched = Vec::new(); let mut matched = Vec::new();
for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in
get_entities!(client) get_entities!(client)
@@ -89,6 +93,8 @@ pub async fn entities(
} }
pub async fn all_players(lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<Vec<Table>> { pub async fn all_players(lua: Lua, client: UserDataRef<Client>, (): ()) -> Result<Vec<Table>> {
let client = unpack!(client);
let mut matched = Vec::new(); let mut matched = Vec::new();
for (id, uuid, kind, position, direction, pose) in get_players!(client) { for (id, uuid, kind, position, direction, pose) in get_players!(client) {
let table = lua.create_table()?; let table = lua.create_table()?;
@@ -108,6 +114,8 @@ pub async fn players(
client: UserDataRef<Client>, client: UserDataRef<Client>,
filter_fn: Function, filter_fn: Function,
) -> Result<Vec<Table>> { ) -> Result<Vec<Table>> {
let client = unpack!(client);
let mut matched = Vec::new(); let mut matched = Vec::new();
for (id, uuid, kind, position, direction, pose) in get_players!(client) { for (id, uuid, kind, position, direction, pose) in get_players!(client) {
let table = lua.create_table()?; let table = lua.create_table()?;

View File

@@ -1,8 +1,8 @@
#[macro_export] #[macro_export]
macro_rules! get_entities { macro_rules! get_entities {
($client:ident) => {{ ($client:ident) => {{
let mut ecs = $client.ecs.write(); let ecs = $client.ecs.read();
ecs.query::<( if let Some(mut query) = ecs.try_query::<(
&AzaleaPosition, &AzaleaPosition,
&CustomName, &CustomName,
&EntityKindComponent, &EntityKindComponent,
@@ -11,31 +11,35 @@ macro_rules! get_entities {
&MinecraftEntityId, &MinecraftEntityId,
Option<&Owneruuid>, Option<&Owneruuid>,
&Pose, &Pose,
)>() )>() {
.iter(&ecs) query
.map( .iter(&ecs)
|(position, custom_name, kind, uuid, direction, id, owner_uuid, pose)| { .map(
( |(position, custom_name, kind, uuid, direction, id, owner_uuid, pose)| {
Vec3::from(*position), (
custom_name.as_ref().map(ToString::to_string), Vec3::from(*position),
kind.to_string(), custom_name.as_ref().map(ToString::to_string),
uuid.to_string(), kind.to_string(),
Direction::from(direction), uuid.to_string(),
id.0, Direction::from(direction),
owner_uuid.map(ToOwned::to_owned), id.0,
*pose as u8, owner_uuid.map(ToOwned::to_owned),
*pose as u8,
)
},
) )
}, .collect::<Vec<_>>()
) } else {
.collect::<Vec<_>>() Vec::new()
}
}}; }};
} }
#[macro_export] #[macro_export]
macro_rules! get_players { macro_rules! get_players {
($client:ident) => {{ ($client:ident) => {{
let mut ecs = $client.ecs.write(); let ecs = $client.ecs.read();
ecs.query_filtered::<( if let Some(mut query) = ecs.try_query_filtered::<(
&MinecraftEntityId, &MinecraftEntityId,
&EntityUuid, &EntityUuid,
&EntityKindComponent, &EntityKindComponent,
@@ -43,17 +47,22 @@ macro_rules! get_players {
&LookDirection, &LookDirection,
&Pose, &Pose,
), (With<Player>, Without<Dead>)>() ), (With<Player>, Without<Dead>)>()
.iter(&ecs) {
.map(|(id, uuid, kind, position, direction, pose)| { query
( .iter(&ecs)
id.0, .map(|(id, uuid, kind, position, direction, pose)| {
uuid.to_string(), (
kind.to_string(), id.0,
Vec3::from(*position), uuid.to_string(),
Direction::from(direction), kind.to_string(),
*pose as u8, Vec3::from(*position),
) Direction::from(direction),
}) *pose as u8,
.collect::<Vec<_>>() )
})
.collect::<Vec<_>>()
} else {
Vec::new()
}
}}; }};
} }