Compare commits

...

4 Commits

Author SHA1 Message Date
5e48377969
fix: add back lua.gc_stop()
There seems to be a (more severe) deadlock again, due to commit
b55207a55936105549d2724c0723a78e9a12c45c. Need to investigate further.
2025-02-28 22:44:22 -05:00
bf9891247f
refactor: style and micro performance improvements 2025-02-28 22:34:01 -05:00
3e74399e5b
refactor: rename tables to table 2025-02-28 22:04:59 -05:00
6c7156f70d
refactor: make some more things async 2025-02-28 22:02:52 -05:00
7 changed files with 99 additions and 81 deletions

View File

@ -14,6 +14,8 @@ use tokio::net::TcpListener;
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow::Result<()> { pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow::Result<()> {
state.lua.gc_stop();
match event { match event {
Event::AddPlayer(player_info) => { Event::AddPlayer(player_info) => {
call_listeners(&state, "add_player", Player::from(player_info)).await; call_listeners(&state, "add_player", Player::from(player_info)).await;
@ -22,10 +24,13 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
let formatted_message = message.message(); let formatted_message = message.message();
info!("{}", formatted_message.to_ansi()); info!("{}", formatted_message.to_ansi());
let owners = state.lua.globals().get::<Vec<String>>("Owners")?;
if message.is_whisper() if message.is_whisper()
&& let (Some(sender), content) = message.split_sender_and_content() && let (Some(sender), content) = message.split_sender_and_content()
&& owners.contains(&sender) && state
.lua
.globals()
.get::<Vec<String>>("Owners")?
.contains(&sender)
{ {
if let Err(error) = state.commands.execute( if let Err(error) = state.commands.execute(
content, content,
@ -55,7 +60,7 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
} }
Event::Disconnect(message) => { Event::Disconnect(message) => {
call_listeners(&state, "disconnect", message.map(|m| m.to_string())).await; call_listeners(&state, "disconnect", message.map(|m| m.to_string())).await;
exit(1) exit(0)
} }
Event::Login => call_listeners(&state, "login", ()).await, Event::Login => call_listeners(&state, "login", ()).await,
Event::RemovePlayer(player_info) => { Event::RemovePlayer(player_info) => {
@ -84,8 +89,7 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
Event::Init => { Event::Init => {
debug!("received initialize event"); debug!("received initialize event");
let globals = state.lua.globals(); state.lua.globals().set(
globals.set(
"client", "client",
lua::client::Client { lua::client::Client {
inner: Some(client), inner: Some(client),

View File

@ -9,7 +9,10 @@ pub fn register_functions(lua: &Lua, globals: &Table) -> Result<()> {
"get_block_from_state", "get_block_from_state",
lua.create_function(get_block_from_state)?, lua.create_function(get_block_from_state)?,
)?; )?;
globals.set("get_block_states", lua.create_function(get_block_states)?)?; globals.set(
"get_block_states",
lua.create_async_function(get_block_states)?,
)?;
Ok(()) Ok(())
} }
@ -18,24 +21,24 @@ pub fn get_block_from_state(lua: &Lua, state: u32) -> Result<Option<Table>> {
let Ok(state) = BlockState::try_from(state) else { let Ok(state) = BlockState::try_from(state) else {
return Ok(None); return Ok(None);
}; };
let b: Box<dyn AzaleaBlock> = state.into(); let block: Box<dyn AzaleaBlock> = state.into();
let bh = b.behavior(); let behavior = block.behavior();
let block = lua.create_table()?; let table = lua.create_table()?;
block.set("id", b.id())?; table.set("id", block.id())?;
block.set("friction", bh.friction)?; table.set("friction", behavior.friction)?;
block.set("jump_factor", bh.jump_factor)?; table.set("jump_factor", behavior.jump_factor)?;
block.set("destroy_time", bh.destroy_time)?; table.set("destroy_time", behavior.destroy_time)?;
block.set("explosion_resistance", bh.explosion_resistance)?; table.set("explosion_resistance", behavior.explosion_resistance)?;
block.set( table.set(
"requires_correct_tool_for_drops", "requires_correct_tool_for_drops",
bh.requires_correct_tool_for_drops, behavior.requires_correct_tool_for_drops,
)?; )?;
Ok(Some(block)) Ok(Some(table))
} }
pub fn get_block_states( pub async fn get_block_states(
lua: &Lua, lua: Lua,
(block_names, filter_fn): (Vec<String>, Option<Function>), (block_names, filter_fn): (Vec<String>, Option<Function>),
) -> Result<Vec<u16>> { ) -> Result<Vec<u16>> {
let mut matched = Vec::new(); let mut matched = Vec::new();
@ -49,7 +52,7 @@ pub fn get_block_states(
p.set("chest_type", b.property::<ChestType>().map(|v| v as u8))?; 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("facing", b.property::<Facing>().map(|v| v as u8))?;
p.set("light_level", b.property::<LightLevel>().map(|v| v as u8))?; p.set("light_level", b.property::<LightLevel>().map(|v| v as u8))?;
filter_fn.call::<bool>(p.clone())? filter_fn.call_async::<bool>(p.clone()).await?
} else { } else {
true true
}) })

View File

@ -59,6 +59,7 @@ impl UserData for Client {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) { fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_async_method("attack", interaction::attack); m.add_async_method("attack", interaction::attack);
m.add_async_method("find_entities", world::find_entities);
m.add_async_method("go_to", movement::go_to); m.add_async_method("go_to", movement::go_to);
m.add_async_method("look_at", movement::look_at); m.add_async_method("look_at", movement::look_at);
m.add_async_method("mine", interaction::mine); m.add_async_method("mine", interaction::mine);
@ -68,7 +69,6 @@ impl UserData for Client {
m.add_method("chat", chat); m.add_method("chat", chat);
m.add_method("disconnect", disconnect); m.add_method("disconnect", disconnect);
m.add_method("find_blocks", world::find_blocks); m.add_method("find_blocks", world::find_blocks);
m.add_method("find_entities", world::find_entities);
m.add_method("get_block_state", world::get_block_state); m.add_method("get_block_state", world::get_block_state);
m.add_method("get_fluid_state", world::get_fluid_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_held_slot", container::set_held_slot);

View File

@ -117,15 +117,15 @@ pub fn jump(_lua: &Lua, client: &mut Client, _: ()) -> Result<()> {
} }
pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> { pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> {
let r = client.component::<HitResultComponent>(); let result = client.component::<HitResultComponent>();
Ok(if r.miss { Ok(if result.miss {
None None
} else { } else {
let result = lua.create_table()?; let table = lua.create_table()?;
result.set("position", Vec3::from(r.block_pos))?; table.set("position", Vec3::from(result.block_pos))?;
result.set("inside", r.inside)?; table.set("inside", result.inside)?;
result.set("world_border", r.world_border)?; table.set("world_border", result.world_border)?;
Some(result) Some(table)
}) })
} }
@ -149,26 +149,29 @@ pub async fn look_at(_lua: Lua, client: UserDataRef<Client>, position: Vec3) ->
} }
pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> { pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> {
let pathfinder = lua.create_table()?; let table = lua.create_table()?;
pathfinder.set( table.set(
"is_calculating", "is_calculating",
client.component::<Pathfinder>().is_calculating, client.component::<Pathfinder>().is_calculating,
)?; )?;
pathfinder.set( table.set(
"is_executing", "is_executing",
if let Some(p) = client.get_component::<ExecutingPath>() { if let Some(pathfinder) = client.get_component::<ExecutingPath>() {
pathfinder.set("last_reached_node", Vec3::from(p.last_reached_node))?; table.set(
pathfinder.set( "last_reached_node",
"last_node_reach_elapsed", Vec3::from(pathfinder.last_reached_node),
p.last_node_reached_at.elapsed().as_millis(),
)?; )?;
pathfinder.set("is_path_partial", p.is_path_partial)?; table.set(
"last_node_reach_elapsed",
pathfinder.last_node_reached_at.elapsed().as_millis(),
)?;
table.set("is_path_partial", pathfinder.is_path_partial)?;
true true
} else { } else {
false false
}, },
)?; )?;
Ok(pathfinder) Ok(table)
} }
pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> { pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> {

View File

@ -16,12 +16,12 @@ pub fn health(_lua: &Lua, client: &Client) -> Result<f32> {
} }
pub fn hunger(lua: &Lua, client: &Client) -> Result<Table> { pub fn hunger(lua: &Lua, client: &Client) -> Result<Table> {
let h = client.hunger(); let hunger = client.hunger();
let hunger = lua.create_table()?; let table = lua.create_table()?;
hunger.set("food", h.food)?; table.set("food", hunger.food)?;
hunger.set("saturation", h.saturation)?; table.set("saturation", hunger.saturation)?;
Ok(hunger) Ok(table)
} }
pub fn score(_lua: &Lua, client: &Client) -> Result<i32> { pub fn score(_lua: &Lua, client: &Client) -> Result<i32> {

View File

@ -10,15 +10,15 @@ use azalea::{
}, },
world::{InstanceName, MinecraftEntityId}, world::{InstanceName, MinecraftEntityId},
}; };
use mlua::{Function, Lua, Result, Table}; use mlua::{Function, Lua, Result, Table, UserDataRef};
pub fn best_tool_for_block(lua: &Lua, client: &Client, block_state: u16) -> Result<Table> { pub fn best_tool_for_block(lua: &Lua, client: &Client, block_state: u16) -> Result<Table> {
let tr = client.best_tool_in_hotbar_for_block(BlockState { id: block_state }); let result = client.best_tool_in_hotbar_for_block(BlockState { id: block_state });
let tool_result = lua.create_table()?; let table = lua.create_table()?;
tool_result.set("index", tr.index)?; table.set("index", result.index)?;
tool_result.set("percentage_per_tick", tr.percentage_per_tick)?; table.set("percentage_per_tick", result.percentage_per_tick)?;
Ok(tool_result) Ok(table)
} }
pub fn dimension(_lua: &Lua, client: &Client) -> Result<String> { pub fn dimension(_lua: &Lua, client: &Client) -> Result<String> {
@ -48,9 +48,14 @@ pub fn find_blocks(
.collect()) .collect())
} }
pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<Vec<Table>> { pub async fn find_entities(
let mut matched = Vec::new(); lua: Lua,
client: UserDataRef<Client>,
filter_fn: Function,
) -> Result<Vec<Table>> {
let mut entities = Vec::new();
{
let mut ecs = client.ecs.lock(); let mut ecs = client.ecs.lock();
let mut query = ecs.query_filtered::<( let mut query = ecs.query_filtered::<(
&MinecraftEntityId, &MinecraftEntityId,
@ -62,7 +67,7 @@ pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<
&Pose, &Pose,
), Without<Dead>>(); ), Without<Dead>>();
for (&id, uuid, kind, custom_name, position, direction, pose) in query.iter(&ecs) { for (id, uuid, kind, custom_name, position, direction, pose) in query.iter(&ecs) {
let entity = lua.create_table()?; let entity = lua.create_table()?;
entity.set("id", id.0)?; entity.set("id", id.0)?;
entity.set("uuid", uuid.to_string())?; entity.set("uuid", uuid.to_string())?;
@ -71,12 +76,16 @@ pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<
entity.set("position", Vec3::from(position))?; entity.set("position", Vec3::from(position))?;
entity.set("direction", Direction::from(direction))?; entity.set("direction", Direction::from(direction))?;
entity.set("pose", *pose as u8)?; entity.set("pose", *pose as u8)?;
entities.push(entity);
if filter_fn.call::<bool>(&entity)? {
matched.push(entity);
} }
} }
let mut matched = Vec::new();
for entity in entities {
if filter_fn.call_async::<bool>(&entity).await? {
matched.push(entity)
}
}
Ok(matched) Ok(matched)
} }
@ -96,16 +105,16 @@ pub fn get_block_state(_lua: &Lua, client: &Client, position: Vec3) -> Result<Op
pub fn get_fluid_state(lua: &Lua, client: &Client, position: Vec3) -> Result<Option<Table>> { pub fn get_fluid_state(lua: &Lua, client: &Client, position: Vec3) -> Result<Option<Table>> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok( Ok(
if let Some(fs) = client.world().read().get_fluid_state(&BlockPos::new( if let Some(state) = client.world().read().get_fluid_state(&BlockPos::new(
position.x as i32, position.x as i32,
position.y as i32, position.y as i32,
position.z as i32, position.z as i32,
)) { )) {
let fluid_state = lua.create_table()?; let table = lua.create_table()?;
fluid_state.set("kind", fs.kind as u8)?; table.set("kind", state.kind as u8)?;
fluid_state.set("amount", fs.amount)?; table.set("amount", state.amount)?;
fluid_state.set("falling", fs.falling)?; table.set("falling", state.falling)?;
Some(fluid_state) Some(table)
} else { } else {
None None
}, },

View File

@ -46,12 +46,11 @@ async fn main() -> anyhow::Result<()> {
console_subscriber::init(); console_subscriber::init();
let args = arguments::Arguments::parse(); let args = arguments::Arguments::parse();
let script_path = args.script.unwrap_or(PathBuf::from(DEFAULT_SCRIPT_PATH)); let script_path = args.script.unwrap_or(PathBuf::from(DEFAULT_SCRIPT_PATH));
let event_listeners = Arc::new(RwLock::new(HashMap::new())); let event_listeners = Arc::new(RwLock::new(HashMap::new()));
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
globals.set("script_path", &*script_path)?; globals.set("script_path", &*script_path)?;
lua::register_functions(&lua, &globals, event_listeners.clone())?; lua::register_functions(&lua, &globals, event_listeners.clone())?;
lua.load( lua.load(