refactor: rename a few things

This commit is contained in:
2025-02-17 11:53:24 -05:00
parent 93348835ac
commit 2373d6500e
21 changed files with 7 additions and 7 deletions

48
src/lua/block.rs Normal file
View File

@@ -0,0 +1,48 @@
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct Block {
pub id: String,
pub friction: f32,
pub jump_factor: f32,
pub destroy_time: f32,
pub explosion_resistance: f32,
pub requires_correct_tool_for_drops: bool,
}
impl IntoLua for Block {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let table = lua.create_table()?;
table.set("id", self.id)?;
table.set("friction", self.friction)?;
table.set("jump_factor", self.jump_factor)?;
table.set("destroy_time", self.destroy_time)?;
table.set("explosion_resistance", self.explosion_resistance)?;
table.set(
"requires_correct_tool_for_drops",
self.requires_correct_tool_for_drops,
)?;
Ok(Value::Table(table))
}
}
impl FromLua for Block {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
id: table.get("id")?,
friction: table.get("friction")?,
jump_factor: table.get("jump_factor")?,
destroy_time: table.get("destroy_time")?,
explosion_resistance: table.get("explosion_resistance")?,
requires_correct_tool_for_drops: table.get("requires_correct_tool_for_drops")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Block".to_string(),
message: None,
})
}
}
}

View File

@@ -0,0 +1,52 @@
use super::{Client, Vec3};
use azalea::{BlockPos, BotClientExt, world::MinecraftEntityId};
use mlua::{Lua, Result, UserDataRef};
pub fn attack(_lua: &Lua, client: &mut Client, entity_id: u32) -> Result<()> {
client
.inner
.as_mut()
.unwrap()
.attack(MinecraftEntityId(entity_id));
Ok(())
}
pub fn block_interact(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)]
client.inner.as_mut().unwrap().block_interact(BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
));
Ok(())
}
pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)]
client
.inner
.clone()
.unwrap()
.mine(BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
))
.await;
Ok(())
}
pub fn set_mining(_lua: &Lua, client: &Client, mining: bool) -> Result<()> {
client.inner.as_ref().unwrap().left_click_mine(mining);
Ok(())
}
pub fn start_mining(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)]
client.inner.as_mut().unwrap().start_mining(BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
));
Ok(())
}

79
src/lua/client/mod.rs Normal file
View File

@@ -0,0 +1,79 @@
mod interaction;
mod movement;
mod state;
mod world;
use super::{
block::Block, direction::Direction, entity::Entity, fluid_state::FluidState, hunger::Hunger,
vec3::Vec3,
};
use azalea::Client as AzaleaClient;
use mlua::{Lua, Result, Table, UserData, UserDataFields, UserDataMethods};
pub struct Client {
pub inner: Option<AzaleaClient>,
}
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("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("hunger", state::hunger);
f.add_field_method_get("looking_at", movement::looking_at);
f.add_field_method_get("pathfinder", movement::pathfinder);
f.add_field_method_get("position", movement::position);
f.add_field_method_get("score", state::score);
f.add_field_method_get("tab_list", tab_list);
f.add_field_method_get("uuid", uuid);
}
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_async_method("mine", interaction::mine);
m.add_async_method("set_client_information", state::set_client_information);
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_mining", interaction::set_mining);
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("set_direction", movement::set_direction);
m.add_method_mut("set_jumping", movement::set_jumping);
m.add_method_mut("sprint", movement::sprint);
m.add_method_mut("start_mining", interaction::start_mining);
m.add_method_mut("walk", movement::walk);
}
}
fn tab_list(lua: &Lua, client: &Client) -> Result<Table> {
let tab_list = lua.create_table()?;
for (uuid, player_info) in client.inner.as_ref().unwrap().tab_list() {
let player = lua.create_table()?;
player.set("gamemode", player_info.gamemode.name())?;
player.set("latency", player_info.latency)?;
player.set("name", player_info.profile.name)?;
player.set(
"display_name",
player_info.display_name.map(|n| n.to_string()),
)?;
tab_list.set(uuid.to_string(), player)?;
}
Ok(tab_list)
}
fn uuid(_lua: &Lua, client: &Client) -> Result<String> {
Ok(client.inner.as_ref().unwrap().uuid().to_string())
}
fn chat(_lua: &Lua, client: &Client, message: String) -> Result<()> {
client.inner.as_ref().unwrap().chat(&message);
Ok(())
}

229
src/lua/client/movement.rs Normal file
View File

@@ -0,0 +1,229 @@
use super::{Client, Direction, Vec3};
use azalea::{
BlockPos, BotClientExt, Client as AzaleaClient, SprintDirection, WalkDirection,
interact::HitResultComponent,
pathfinder::{
ExecutingPath, Pathfinder, PathfinderClientExt,
goals::{BlockPosGoal, Goal, RadiusGoal, ReachBlockPosGoal, XZGoal, YGoal},
},
};
use mlua::{FromLua, Lua, Result, Table, Value};
pub fn direction(_lua: &Lua, client: &Client) -> Result<Direction> {
let d = client.inner.as_ref().unwrap().direction();
Ok(Direction { x: d.0, y: d.1 })
}
pub fn eye_position(_lua: &Lua, client: &Client) -> Result<Vec3> {
let p = client.inner.as_ref().unwrap().eye_position();
Ok(Vec3 {
x: p.x,
y: p.y,
z: p.z,
})
}
pub fn goto(
lua: &Lua,
client: &mut Client,
(data, metadata): (Value, Option<Table>),
) -> Result<()> {
fn g(client: &AzaleaClient, without_mining: bool, goal: impl Goal + Send + Sync + 'static) {
if without_mining {
client.goto_without_mining(goal);
} else {
client.goto(goal);
}
}
let error = mlua::Error::FromLuaConversionError {
from: data.type_name(),
to: "Table".to_string(),
message: None,
};
let client = client.inner.as_ref().unwrap();
let (goal_type, without_mining) = metadata
.map(|t| {
(
t.get("type").unwrap_or_default(),
t.get("without_mining").unwrap_or_default(),
)
})
.unwrap_or_default();
#[allow(clippy::cast_possible_truncation)]
match goal_type {
1 => {
let t = data.as_table().ok_or(error)?;
let p = Vec3::from_lua(t.get("position")?, lua)?;
g(
client,
without_mining,
RadiusGoal {
pos: azalea::Vec3::new(p.x, p.y, p.z),
radius: t.get("radius")?,
},
);
}
2 => {
let p = Vec3::from_lua(data, lua)?;
g(
client,
without_mining,
ReachBlockPosGoal {
pos: BlockPos::new(p.x as i32, p.y as i32, p.z as i32),
chunk_storage: client.world().read().chunks.clone(),
},
);
}
3 => {
let t = data.as_table().ok_or(error)?;
g(
client,
without_mining,
XZGoal {
x: t.get("x")?,
z: t.get("z")?,
},
);
}
4 => g(
client,
without_mining,
YGoal {
y: data.as_integer().ok_or(error)?,
},
),
_ => {
let p = Vec3::from_lua(data, lua)?;
g(
client,
without_mining,
BlockPosGoal(BlockPos::new(p.x as i32, p.y as i32, p.z as i32)),
);
}
}
Ok(())
}
pub fn jump(_lua: &Lua, client: &mut Client, _: ()) -> Result<()> {
client.inner.as_mut().unwrap().jump();
Ok(())
}
pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> {
let hr = client
.inner
.as_ref()
.unwrap()
.component::<HitResultComponent>();
Ok(if hr.miss {
None
} else {
let result = lua.create_table()?;
result.set(
"position",
Vec3 {
x: f64::from(hr.block_pos.x),
y: f64::from(hr.block_pos.y),
z: f64::from(hr.block_pos.z),
},
)?;
result.set("inside", hr.inside)?;
result.set("world_border", hr.world_border)?;
Some(result)
})
}
pub fn look_at(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
client
.inner
.as_mut()
.unwrap()
.look_at(azalea::Vec3::new(position.x, position.y, position.z));
Ok(())
}
pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> {
let client = client.inner.as_ref().unwrap();
let pathfinder = lua.create_table()?;
pathfinder.set(
"is_calculating",
client.component::<Pathfinder>().is_calculating,
)?;
pathfinder.set(
"is_executing",
if let Some(p) = client.get_component::<ExecutingPath>() {
pathfinder.set(
"last_reached_node",
Vec3 {
x: f64::from(p.last_reached_node.x),
y: f64::from(p.last_reached_node.y),
z: f64::from(p.last_reached_node.z),
},
)?;
pathfinder.set(
"last_node_reach_elapsed",
p.last_node_reached_at.elapsed().as_millis(),
)?;
pathfinder.set("is_path_partial", p.is_path_partial)?;
true
} else {
false
},
)?;
Ok(pathfinder)
}
pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> {
let p = client.inner.as_ref().unwrap().position();
Ok(Vec3 {
x: p.x,
y: p.y,
z: p.z,
})
}
pub fn set_direction(_lua: &Lua, client: &mut Client, direction: (f32, f32)) -> Result<()> {
client
.inner
.as_mut()
.unwrap()
.set_direction(direction.0, direction.1);
Ok(())
}
pub fn set_jumping(_lua: &Lua, client: &mut Client, jumping: bool) -> Result<()> {
client.inner.as_mut().unwrap().set_jumping(jumping);
Ok(())
}
pub fn sprint(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> {
client.inner.as_mut().unwrap().sprint(match direction {
5 => SprintDirection::ForwardRight,
6 => SprintDirection::ForwardLeft,
_ => SprintDirection::Forward,
});
Ok(())
}
pub fn stop_pathfinding(_lua: &Lua, client: &Client, _: ()) -> Result<()> {
client.inner.as_ref().unwrap().stop_pathfinding();
Ok(())
}
pub fn walk(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> {
client.inner.as_mut().unwrap().walk(match direction {
1 => WalkDirection::Forward,
2 => WalkDirection::Backward,
3 => WalkDirection::Left,
4 => WalkDirection::Right,
5 => WalkDirection::ForwardRight,
6 => WalkDirection::ForwardLeft,
7 => WalkDirection::BackwardRight,
8 => WalkDirection::BackwardLeft,
_ => WalkDirection::None,
});
Ok(())
}

44
src/lua/client/state.rs Normal file
View File

@@ -0,0 +1,44 @@
use super::{Client, Hunger};
use azalea::{
ClientInformation,
entity::metadata::{AirSupply, Score},
};
use mlua::{Lua, Result, Table, UserDataRef};
pub fn air_supply(_lua: &Lua, client: &Client) -> Result<i32> {
Ok(client.inner.as_ref().unwrap().component::<AirSupply>().0)
}
pub fn health(_lua: &Lua, client: &Client) -> Result<f32> {
Ok(client.inner.as_ref().unwrap().health())
}
pub fn hunger(_lua: &Lua, client: &Client) -> Result<Hunger> {
let h = client.inner.as_ref().unwrap().hunger();
Ok(Hunger {
food: h.food,
saturation: h.saturation,
})
}
pub fn score(_lua: &Lua, client: &Client) -> Result<i32> {
Ok(client.inner.as_ref().unwrap().component::<Score>().0)
}
pub async fn set_client_information(
_lua: Lua,
client: UserDataRef<Client>,
client_information: Table,
) -> Result<()> {
client
.inner
.as_ref()
.unwrap()
.set_client_information(ClientInformation {
view_distance: client_information.get("view_distance")?,
..ClientInformation::default()
})
.await
.unwrap();
Ok(())
}

132
src/lua/client/world.rs Normal file
View File

@@ -0,0 +1,132 @@
use super::{Block, Client, Entity, FluidState, Vec3};
use azalea::{
BlockPos,
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};
pub fn find_blocks(
_lua: &Lua,
client: &Client,
(nearest_to, block_names): (Vec3, Vec<String>),
) -> Result<Vec<Vec3>> {
#[allow(clippy::cast_possible_truncation)]
Ok(client
.inner
.as_ref()
.unwrap()
.world()
.read()
.find_blocks(
BlockPos::new(
nearest_to.x as i32,
nearest_to.y as i32,
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(),
},
)
.map(|p| Vec3 {
x: f64::from(p.x),
y: f64::from(p.y),
z: f64::from(p.z),
})
.collect())
}
pub fn find_entities(_lua: &Lua, client: &Client, filter_fn: Function) -> Result<Vec<Entity>> {
let mut matched = Vec::new();
let mut ecs = client.inner.as_ref().unwrap().ecs.lock();
let mut query = ecs.query_filtered::<(
&MinecraftEntityId,
&EntityUuid,
&EntityKind,
&AzaleaPosition,
&CustomName,
), Without<Dead>>();
for (&id, uuid, kind, position, custom_name) in query.iter(&ecs) {
let entity = Entity {
id: id.0,
uuid: uuid.to_string(),
kind: kind.to_string(),
position: Vec3 {
x: position.x,
y: position.y,
z: position.z,
},
custom_name: custom_name.as_ref().map(ToString::to_string),
};
if filter_fn.call::<bool>(entity.clone()).unwrap() {
matched.push(entity);
}
}
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
.inner
.as_ref()
.unwrap()
.world()
.read()
.get_block_state(&BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
))
.map(|b| b.id))
}
pub fn get_fluid_state(_lua: &Lua, client: &Client, position: Vec3) -> Result<Option<FluidState>> {
#[allow(clippy::cast_possible_truncation)]
Ok(client
.inner
.as_ref()
.unwrap()
.world()
.read()
.get_fluid_state(&BlockPos::new(
position.x as i32,
position.y as i32,
position.z as i32,
))
.map(|f| FluidState {
kind: f.kind as u8,
amount: f.amount,
falling: f.falling,
}))
}

33
src/lua/direction.rs Normal file
View File

@@ -0,0 +1,33 @@
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct Direction {
pub x: f32,
pub y: f32,
}
impl IntoLua for Direction {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let table = lua.create_table()?;
table.set("x", self.x)?;
table.set("y", self.y)?;
Ok(Value::Table(table))
}
}
impl FromLua for Direction {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
x: table.get("x")?,
y: table.get("y")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Direction".to_string(),
message: None,
})
}
}
}

43
src/lua/entity.rs Normal file
View File

@@ -0,0 +1,43 @@
use super::vec3::Vec3;
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct Entity {
pub id: u32,
pub uuid: String,
pub kind: String,
pub position: Vec3,
pub custom_name: Option<String>,
}
impl IntoLua for Entity {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let entity = lua.create_table()?;
entity.set("id", self.id)?;
entity.set("uuid", self.uuid)?;
entity.set("kind", self.kind)?;
entity.set("position", self.position)?;
entity.set("custom_name", self.custom_name)?;
Ok(Value::Table(entity))
}
}
impl FromLua for Entity {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
id: table.get("id")?,
uuid: table.get("uuid")?,
kind: table.get("kind")?,
position: table.get("position")?,
custom_name: table.get("custom_name")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Position".to_string(),
message: None,
})
}
}
}

36
src/lua/fluid_state.rs Normal file
View File

@@ -0,0 +1,36 @@
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct FluidState {
pub kind: u8,
pub amount: u8,
pub falling: bool,
}
impl IntoLua for FluidState {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let table = lua.create_table()?;
table.set("kind", self.kind)?;
table.set("amount", self.amount)?;
table.set("falling", self.falling)?;
Ok(Value::Table(table))
}
}
impl FromLua for FluidState {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
kind: table.get("kind")?,
amount: table.get("amount")?,
falling: table.get("falling")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "FluidState".to_string(),
message: None,
})
}
}
}

33
src/lua/hunger.rs Normal file
View File

@@ -0,0 +1,33 @@
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct Hunger {
pub food: u32,
pub saturation: f32,
}
impl IntoLua for Hunger {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let table = lua.create_table()?;
table.set("food", self.food)?;
table.set("saturation", self.saturation)?;
Ok(Value::Table(table))
}
}
impl FromLua for Hunger {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
food: table.get("food")?,
saturation: table.get("saturation")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Hunger".to_string(),
message: None,
})
}
}
}

42
src/lua/logging.rs Normal file
View File

@@ -0,0 +1,42 @@
use log::{debug, error, info, trace, warn};
use mlua::{Lua, Result, Table};
pub fn register_functions(lua: &Lua, globals: &Table) -> Result<()> {
globals.set(
"error",
lua.create_function(|_, message: String| {
error!("{message}");
Ok(())
})?,
)?;
globals.set(
"warn",
lua.create_function(|_, message: String| {
warn!("{message}");
Ok(())
})?,
)?;
globals.set(
"info",
lua.create_function(|_, message: String| {
info!("{message}");
Ok(())
})?,
)?;
globals.set(
"debug",
lua.create_function(|_, message: String| {
debug!("{message}");
Ok(())
})?,
)?;
globals.set(
"trace",
lua.create_function(|_, message: String| {
trace!("{message}");
Ok(())
})?,
)?;
Ok(())
}

56
src/lua/mod.rs Normal file
View File

@@ -0,0 +1,56 @@
pub mod block;
pub mod client;
pub mod direction;
pub mod entity;
pub mod fluid_state;
pub mod hunger;
pub mod logging;
pub mod vec3;
use mlua::{Lua, Table};
#[derive(Debug)]
#[allow(dead_code)]
pub enum Error {
EvalChunk(mlua::Error),
ExecChunk(mlua::Error),
LoadChunk(mlua::Error),
MissingPath(mlua::Error),
ReadFile(std::io::Error),
}
pub fn register_functions(lua: &Lua, globals: &Table) -> mlua::Result<()> {
globals.set(
"sleep",
lua.create_async_function(async |_, duration: u64| {
tokio::time::sleep(std::time::Duration::from_millis(duration)).await;
Ok(())
})?,
)?;
logging::register_functions(lua, globals)
}
pub fn reload(lua: &Lua) -> Result<(), Error> {
lua.load(
&std::fs::read_to_string(
lua.globals()
.get::<String>("script_path")
.map_err(Error::MissingPath)?,
)
.map_err(Error::ReadFile)?,
)
.exec()
.map_err(Error::LoadChunk)
}
pub async fn eval(lua: &Lua, code: &str) -> Result<String, Error> {
lua.load(code)
.eval_async::<String>()
.await
.map_err(Error::EvalChunk)
}
pub async fn exec(lua: &Lua, code: &str) -> Result<(), Error> {
lua.load(code).exec_async().await.map_err(Error::ExecChunk)
}

36
src/lua/vec3.rs Normal file
View File

@@ -0,0 +1,36 @@
use mlua::{FromLua, IntoLua, Lua, Result, Value};
#[derive(Clone)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl IntoLua for Vec3 {
fn into_lua(self, lua: &Lua) -> Result<Value> {
let table = lua.create_table()?;
table.set("x", self.x)?;
table.set("y", self.y)?;
table.set("z", self.z)?;
Ok(Value::Table(table))
}
}
impl FromLua for Vec3 {
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
if let Value::Table(table) = value {
Ok(Self {
x: table.get("x")?,
y: table.get("y")?,
z: table.get("z")?,
})
} else {
Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Position".to_string(),
message: None,
})
}
}
}