refactor(client): simplify usage with deref

This commit is contained in:
Ryan 2025-02-18 21:34:57 -05:00
parent 93a2dda8c6
commit 75d4a9c183
Signed by: ErrorNoInternet
GPG Key ID: 2486BFB7B1E6A4A3
7 changed files with 68 additions and 108 deletions

View File

@ -59,7 +59,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
commands.register( commands.register(
literal("eval").then(argument("code", string()).executes(|ctx: &Ctx| { literal("eval").then(argument("code", string()).executes(|ctx: &Ctx| {
let source = ctx.source.clone(); let source = ctx.source.clone();
let code = get_string(ctx, "code").unwrap(); let code = get_string(ctx, "code").expect("argument should exist");
tokio::spawn(async move { tokio::spawn(async move {
let source = source.lock().await; let source = source.lock().await;
source.reply(&format!("{:?}", eval(&source.state.lua, &code).await)); source.reply(&format!("{:?}", eval(&source.state.lua, &code).await));
@ -71,7 +71,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
commands.register( commands.register(
literal("exec").then(argument("code", string()).executes(|ctx: &Ctx| { literal("exec").then(argument("code", string()).executes(|ctx: &Ctx| {
let source = ctx.source.clone(); let source = ctx.source.clone();
let code = get_string(ctx, "code").unwrap(); let code = get_string(ctx, "code").expect("argument should exist");
tokio::spawn(async move { tokio::spawn(async move {
let source = source.lock().await; let source = source.lock().await;
source.reply(&format!("{:?}", exec(&source.state.lua, &code).await)); source.reply(&format!("{:?}", exec(&source.state.lua, &code).await));

View File

@ -8,31 +8,18 @@ use mlua::{Lua, Result, UserDataRef};
pub fn container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> { pub fn container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> {
Ok(client Ok(client
.inner
.as_ref()
.unwrap()
.get_open_container() .get_open_container()
.map(|c| ContainerRef { inner: c })) .map(|c| ContainerRef { inner: c }))
} }
pub fn held_item(_lua: &Lua, client: &Client) -> Result<ItemStack> { pub fn held_item(_lua: &Lua, client: &Client) -> Result<ItemStack> {
Ok(ItemStack { Ok(ItemStack {
inner: client inner: client.component::<Inventory>().held_item(),
.inner
.as_ref()
.unwrap()
.component::<Inventory>()
.held_item(),
}) })
} }
pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> { pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> {
Ok(client Ok(client.component::<Inventory>().selected_hotbar_slot)
.inner
.as_ref()
.unwrap()
.component::<Inventory>()
.selected_hotbar_slot)
} }
pub async fn open_container_at( pub async fn open_container_at(
@ -42,9 +29,7 @@ pub async fn open_container_at(
) -> Result<Option<Container>> { ) -> Result<Option<Container>> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok(client Ok(client
.inner
.clone() .clone()
.unwrap()
.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,
@ -55,12 +40,7 @@ pub async fn open_container_at(
} }
pub fn open_inventory(_lua: &Lua, client: &mut Client, _: ()) -> Result<Option<Container>> { pub fn open_inventory(_lua: &Lua, client: &mut Client, _: ()) -> Result<Option<Container>> {
Ok(client Ok(client.open_inventory().map(|c| Container { inner: c }))
.inner
.as_mut()
.unwrap()
.open_inventory()
.map(|c| Container { inner: c }))
} }
pub fn set_held_slot(_lua: &Lua, client: &Client, slot: u8) -> Result<()> { pub fn set_held_slot(_lua: &Lua, client: &Client, slot: u8) -> Result<()> {
@ -68,7 +48,6 @@ pub fn set_held_slot(_lua: &Lua, client: &Client, slot: u8) -> Result<()> {
return Ok(()); return Ok(());
} }
let client = client.inner.as_ref().unwrap();
{ {
let mut ecs = client.ecs.lock(); let mut ecs = client.ecs.lock();
let mut inventory = client.query::<&mut Inventory>(&mut ecs); let mut inventory = client.query::<&mut Inventory>(&mut ecs);

View File

@ -3,17 +3,13 @@ use azalea::{BlockPos, BotClientExt, world::MinecraftEntityId};
use mlua::{Lua, Result, UserDataRef}; use mlua::{Lua, Result, UserDataRef};
pub fn attack(_lua: &Lua, client: &mut Client, entity_id: u32) -> Result<()> { pub fn attack(_lua: &Lua, client: &mut Client, entity_id: u32) -> Result<()> {
client client.attack(MinecraftEntityId(entity_id));
.inner
.as_mut()
.unwrap()
.attack(MinecraftEntityId(entity_id));
Ok(()) Ok(())
} }
pub fn block_interact(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> { pub fn block_interact(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
client.inner.as_mut().unwrap().block_interact(BlockPos::new( client.block_interact(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,
@ -22,15 +18,13 @@ pub fn block_interact(_lua: &Lua, client: &mut Client, position: Vec3) -> Result
} }
pub fn has_attack_cooldown(_lua: &Lua, client: &Client) -> Result<bool> { pub fn has_attack_cooldown(_lua: &Lua, client: &Client) -> Result<bool> {
Ok(client.inner.as_ref().unwrap().has_attack_cooldown()) Ok(client.has_attack_cooldown())
} }
pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Result<()> { pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
client client
.inner
.clone() .clone()
.unwrap()
.mine(BlockPos::new( .mine(BlockPos::new(
position.x as i32, position.x as i32,
position.y as i32, position.y as i32,
@ -41,13 +35,13 @@ pub async fn mine(_lua: Lua, client: UserDataRef<Client>, position: Vec3) -> Res
} }
pub fn set_mining(_lua: &Lua, client: &Client, mining: bool) -> Result<()> { pub fn set_mining(_lua: &Lua, client: &Client, mining: bool) -> Result<()> {
client.inner.as_ref().unwrap().left_click_mine(mining); client.left_click_mine(mining);
Ok(()) Ok(())
} }
pub fn start_mining(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> { pub fn start_mining(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
client.inner.as_mut().unwrap().start_mining(BlockPos::new( client.start_mining(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,

View File

@ -5,18 +5,36 @@ mod state;
mod world; mod world;
use super::{ use super::{
container::item_stack::ItemStack, container::{Container, ContainerRef, item_stack::ItemStack},
container::{Container, ContainerRef},
direction::Direction, direction::Direction,
vec3::Vec3, vec3::Vec3,
}; };
use azalea::Client as AzaleaClient; use azalea::Client as AzaleaClient;
use mlua::{Lua, Result, Table, UserData, UserDataFields, UserDataMethods}; use mlua::{Lua, Result, Table, UserData, UserDataFields, UserDataMethods};
use std::ops::{Deref, DerefMut};
pub struct Client { pub struct Client {
pub inner: Option<AzaleaClient>, pub inner: Option<AzaleaClient>,
} }
impl Deref for Client {
type Target = AzaleaClient;
fn deref(&self) -> &Self::Target {
self.inner
.as_ref()
.expect("should have received init event")
}
}
impl DerefMut for Client {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
.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);
@ -65,18 +83,18 @@ impl UserData for Client {
} }
fn chat(_lua: &Lua, client: &Client, message: String) -> Result<()> { fn chat(_lua: &Lua, client: &Client, message: String) -> Result<()> {
client.inner.as_ref().unwrap().chat(&message); client.chat(&message);
Ok(()) Ok(())
} }
fn disconnect(_lua: &Lua, client: &Client, _: ()) -> Result<()> { fn disconnect(_lua: &Lua, client: &Client, _: ()) -> Result<()> {
client.inner.as_ref().unwrap().disconnect(); client.disconnect();
Ok(()) Ok(())
} }
fn tab_list(lua: &Lua, client: &Client) -> Result<Table> { fn tab_list(lua: &Lua, client: &Client) -> Result<Table> {
let tab_list = lua.create_table()?; let tab_list = lua.create_table()?;
for (uuid, player_info) in client.inner.as_ref().unwrap().tab_list() { for (uuid, player_info) in client.tab_list() {
let player = lua.create_table()?; let player = lua.create_table()?;
player.set("gamemode", player_info.gamemode.name())?; player.set("gamemode", player_info.gamemode.name())?;
player.set("latency", player_info.latency)?; player.set("latency", player_info.latency)?;
@ -91,5 +109,5 @@ fn tab_list(lua: &Lua, client: &Client) -> Result<Table> {
} }
fn uuid(_lua: &Lua, client: &Client) -> Result<String> { fn uuid(_lua: &Lua, client: &Client) -> Result<String> {
Ok(client.inner.as_ref().unwrap().uuid().to_string()) Ok(client.uuid().to_string())
} }

View File

@ -1,6 +1,6 @@
use super::{Client, Direction, Vec3}; use super::{Client, Direction, Vec3};
use azalea::{ use azalea::{
BlockPos, BotClientExt, Client as AzaleaClient, SprintDirection, WalkDirection, BlockPos, BotClientExt, SprintDirection, WalkDirection,
interact::HitResultComponent, interact::HitResultComponent,
pathfinder::{ pathfinder::{
ExecutingPath, GotoEvent, Pathfinder, PathfinderClientExt, ExecutingPath, GotoEvent, Pathfinder, PathfinderClientExt,
@ -10,12 +10,12 @@ use azalea::{
use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value}; use mlua::{FromLua, Lua, Result, Table, UserDataRef, Value};
pub fn direction(_lua: &Lua, client: &Client) -> Result<Direction> { pub fn direction(_lua: &Lua, client: &Client) -> Result<Direction> {
let d = client.inner.as_ref().unwrap().direction(); let d = client.direction();
Ok(Direction { x: d.0, y: d.1 }) Ok(Direction { x: d.0, y: d.1 })
} }
pub fn eye_position(_lua: &Lua, client: &Client) -> Result<Vec3> { pub fn eye_position(_lua: &Lua, client: &Client) -> Result<Vec3> {
let p = client.inner.as_ref().unwrap().eye_position(); let p = client.eye_position();
Ok(Vec3 { Ok(Vec3 {
x: p.x, x: p.x,
y: p.y, y: p.y,
@ -28,7 +28,7 @@ pub async fn goto(
client: UserDataRef<Client>, client: UserDataRef<Client>,
(data, metadata): (Value, Option<Table>), (data, metadata): (Value, Option<Table>),
) -> Result<()> { ) -> Result<()> {
fn g(client: &AzaleaClient, without_mining: bool, goal: impl Goal + Send + Sync + 'static) { fn g(client: &Client, without_mining: bool, goal: impl Goal + Send + Sync + 'static) {
if without_mining { if without_mining {
client.goto_without_mining(goal); client.goto_without_mining(goal);
} else { } else {
@ -41,7 +41,6 @@ pub async fn goto(
to: "Table".to_string(), to: "Table".to_string(),
message: None, message: None,
}; };
let client = client.inner.as_ref().unwrap();
let (goal_type, without_mining) = metadata let (goal_type, without_mining) = metadata
.map(|t| { .map(|t| {
( (
@ -57,7 +56,7 @@ pub async fn goto(
let t = data.as_table().ok_or(error)?; let t = data.as_table().ok_or(error)?;
let p = Vec3::from_lua(t.get("position")?, &lua)?; let p = Vec3::from_lua(t.get("position")?, &lua)?;
g( g(
client, &client,
without_mining, without_mining,
RadiusGoal { RadiusGoal {
pos: azalea::Vec3::new(p.x, p.y, p.z), pos: azalea::Vec3::new(p.x, p.y, p.z),
@ -68,7 +67,7 @@ pub async fn goto(
2 => { 2 => {
let p = Vec3::from_lua(data, &lua)?; let p = Vec3::from_lua(data, &lua)?;
g( g(
client, &client,
without_mining, without_mining,
ReachBlockPosGoal { ReachBlockPosGoal {
pos: BlockPos::new(p.x as i32, p.y as i32, p.z as i32), pos: BlockPos::new(p.x as i32, p.y as i32, p.z as i32),
@ -79,7 +78,7 @@ pub async fn goto(
3 => { 3 => {
let t = data.as_table().ok_or(error)?; let t = data.as_table().ok_or(error)?;
g( g(
client, &client,
without_mining, without_mining,
XZGoal { XZGoal {
x: t.get("x")?, x: t.get("x")?,
@ -88,7 +87,7 @@ pub async fn goto(
); );
} }
4 => g( 4 => g(
client, &client,
without_mining, without_mining,
YGoal { YGoal {
y: data.as_integer().ok_or(error)?, y: data.as_integer().ok_or(error)?,
@ -97,7 +96,7 @@ pub async fn goto(
_ => { _ => {
let p = Vec3::from_lua(data, &lua)?; let p = Vec3::from_lua(data, &lua)?;
g( g(
client, &client,
without_mining, without_mining,
BlockPosGoal(BlockPos::new(p.x as i32, p.y as i32, p.z as i32)), BlockPosGoal(BlockPos::new(p.x as i32, p.y as i32, p.z as i32)),
); );
@ -114,16 +113,12 @@ pub async fn goto(
} }
pub fn jump(_lua: &Lua, client: &mut Client, _: ()) -> Result<()> { pub fn jump(_lua: &Lua, client: &mut Client, _: ()) -> Result<()> {
client.inner.as_mut().unwrap().jump(); client.jump();
Ok(()) Ok(())
} }
pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> { pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> {
let hr = client let hr = client.component::<HitResultComponent>();
.inner
.as_ref()
.unwrap()
.component::<HitResultComponent>();
Ok(if hr.miss { Ok(if hr.miss {
None None
} else { } else {
@ -143,16 +138,11 @@ pub fn looking_at(lua: &Lua, client: &Client) -> Result<Option<Table>> {
} }
pub fn look_at(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> { pub fn look_at(_lua: &Lua, client: &mut Client, position: Vec3) -> Result<()> {
client client.look_at(azalea::Vec3::new(position.x, position.y, position.z));
.inner
.as_mut()
.unwrap()
.look_at(azalea::Vec3::new(position.x, position.y, position.z));
Ok(()) Ok(())
} }
pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> { pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> {
let client = client.inner.as_ref().unwrap();
let pathfinder = lua.create_table()?; let pathfinder = lua.create_table()?;
pathfinder.set( pathfinder.set(
"is_calculating", "is_calculating",
@ -183,7 +173,7 @@ pub fn pathfinder(lua: &Lua, client: &Client) -> Result<Table> {
} }
pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> { pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> {
let p = client.inner.as_ref().unwrap().position(); let p = client.position();
Ok(Vec3 { Ok(Vec3 {
x: p.x, x: p.x,
y: p.y, y: p.y,
@ -192,21 +182,17 @@ pub fn position(_lua: &Lua, client: &Client) -> Result<Vec3> {
} }
pub fn set_direction(_lua: &Lua, client: &mut Client, direction: (f32, f32)) -> Result<()> { pub fn set_direction(_lua: &Lua, client: &mut Client, direction: (f32, f32)) -> Result<()> {
client client.set_direction(direction.0, direction.1);
.inner
.as_mut()
.unwrap()
.set_direction(direction.0, direction.1);
Ok(()) Ok(())
} }
pub fn set_jumping(_lua: &Lua, client: &mut Client, jumping: bool) -> Result<()> { pub fn set_jumping(_lua: &Lua, client: &mut Client, jumping: bool) -> Result<()> {
client.inner.as_mut().unwrap().set_jumping(jumping); client.set_jumping(jumping);
Ok(()) Ok(())
} }
pub fn sprint(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> { pub fn sprint(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> {
client.inner.as_mut().unwrap().sprint(match direction { client.sprint(match direction {
5 => SprintDirection::ForwardRight, 5 => SprintDirection::ForwardRight,
6 => SprintDirection::ForwardLeft, 6 => SprintDirection::ForwardLeft,
_ => SprintDirection::Forward, _ => SprintDirection::Forward,
@ -215,12 +201,12 @@ pub fn sprint(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> {
} }
pub fn stop_pathfinding(_lua: &Lua, client: &Client, _: ()) -> Result<()> { pub fn stop_pathfinding(_lua: &Lua, client: &Client, _: ()) -> Result<()> {
client.inner.as_ref().unwrap().stop_pathfinding(); client.stop_pathfinding();
Ok(()) Ok(())
} }
pub fn walk(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> { pub fn walk(_lua: &Lua, client: &mut Client, direction: u8) -> Result<()> {
client.inner.as_mut().unwrap().walk(match direction { client.walk(match direction {
1 => WalkDirection::Forward, 1 => WalkDirection::Forward,
2 => WalkDirection::Backward, 2 => WalkDirection::Backward,
3 => WalkDirection::Left, 3 => WalkDirection::Left,

View File

@ -3,18 +3,19 @@ use azalea::{
ClientInformation, ClientInformation,
entity::metadata::{AirSupply, Score}, entity::metadata::{AirSupply, Score},
}; };
use log::error;
use mlua::{Lua, Result, Table, UserDataRef}; use mlua::{Lua, Result, Table, UserDataRef};
pub fn air_supply(_lua: &Lua, client: &Client) -> Result<i32> { pub fn air_supply(_lua: &Lua, client: &Client) -> Result<i32> {
Ok(client.inner.as_ref().unwrap().component::<AirSupply>().0) Ok(client.component::<AirSupply>().0)
} }
pub fn health(_lua: &Lua, client: &Client) -> Result<f32> { pub fn health(_lua: &Lua, client: &Client) -> Result<f32> {
Ok(client.inner.as_ref().unwrap().health()) Ok(client.health())
} }
pub fn hunger(lua: &Lua, client: &Client) -> Result<Table> { pub fn hunger(lua: &Lua, client: &Client) -> Result<Table> {
let h = client.inner.as_ref().unwrap().hunger(); let h = client.hunger();
let hunger = lua.create_table()?; let hunger = lua.create_table()?;
hunger.set("food", h.food)?; hunger.set("food", h.food)?;
@ -23,7 +24,7 @@ pub fn hunger(lua: &Lua, client: &Client) -> Result<Table> {
} }
pub fn score(_lua: &Lua, client: &Client) -> Result<i32> { pub fn score(_lua: &Lua, client: &Client) -> Result<i32> {
Ok(client.inner.as_ref().unwrap().component::<Score>().0) Ok(client.component::<Score>().0)
} }
pub async fn set_client_information( pub async fn set_client_information(
@ -31,15 +32,14 @@ pub async fn set_client_information(
client: UserDataRef<Client>, client: UserDataRef<Client>,
client_information: Table, client_information: Table,
) -> Result<()> { ) -> Result<()> {
client if let Err(error) = client
.inner
.as_ref()
.unwrap()
.set_client_information(ClientInformation { .set_client_information(ClientInformation {
view_distance: client_information.get("view_distance")?, view_distance: client_information.get("view_distance")?,
..ClientInformation::default() ..ClientInformation::default()
}) })
.await .await
.unwrap(); {
error!("failed to set client client information: {error:?}");
}
Ok(()) Ok(())
} }

View File

@ -10,11 +10,7 @@ use azalea::{
use mlua::{Function, Lua, Result, Table}; use mlua::{Function, Lua, Result, Table};
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 let tr = client.best_tool_in_hotbar_for_block(BlockState { id: block_state });
.inner
.as_ref()
.unwrap()
.best_tool_in_hotbar_for_block(BlockState { id: block_state });
let tool_result = lua.create_table()?; let tool_result = lua.create_table()?;
tool_result.set("index", tr.index)?; tool_result.set("index", tr.index)?;
@ -29,9 +25,6 @@ pub fn find_blocks(
) -> Result<Vec<Vec3>> { ) -> Result<Vec<Vec3>> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok(client Ok(client
.inner
.as_ref()
.unwrap()
.world() .world()
.read() .read()
.find_blocks( .find_blocks(
@ -55,7 +48,7 @@ pub fn find_blocks(
pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<Vec<Table>> { pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<Vec<Table>> {
let mut matched = Vec::new(); let mut matched = Vec::new();
let mut ecs = client.inner.as_ref().unwrap().ecs.lock(); let mut ecs = client.ecs.lock();
let mut query = ecs.query_filtered::<( let mut query = ecs.query_filtered::<(
&MinecraftEntityId, &MinecraftEntityId,
&EntityUuid, &EntityUuid,
@ -90,9 +83,6 @@ pub fn find_entities(lua: &Lua, client: &Client, filter_fn: Function) -> Result<
pub fn get_block_state(_lua: &Lua, client: &Client, position: Vec3) -> Result<Option<u16>> { pub fn get_block_state(_lua: &Lua, client: &Client, position: Vec3) -> Result<Option<u16>> {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok(client Ok(client
.inner
.as_ref()
.unwrap()
.world() .world()
.read() .read()
.get_block_state(&BlockPos::new( .get_block_state(&BlockPos::new(
@ -106,18 +96,11 @@ 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 if let Some(fs) = client.world().read().get_fluid_state(&BlockPos::new(
.inner
.as_ref()
.unwrap()
.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 fluid_state = lua.create_table()?;
fluid_state.set("kind", fs.kind as u8)?; fluid_state.set("kind", fs.kind as u8)?;
fluid_state.set("amount", fs.amount)?; fluid_state.set("amount", fs.amount)?;