feat(client): add world interaction methods
This commit is contained in:
parent
8e89f8e9ea
commit
04b0bdd756
48
src/scripting/block.rs
Normal file
48
src/scripting/block.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,17 @@
|
|||||||
mod interaction;
|
mod interaction;
|
||||||
mod movement;
|
mod movement;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod world;
|
||||||
|
|
||||||
use super::{direction::Direction, entity::Entity, hunger::Hunger, vec3::Vec3};
|
use super::{
|
||||||
|
block::Block, direction::Direction, entity::Entity, fluid_state::FluidState, hunger::Hunger,
|
||||||
|
vec3::Vec3,
|
||||||
|
};
|
||||||
use azalea::{
|
use azalea::{
|
||||||
Client as AzaleaClient,
|
Client as AzaleaClient,
|
||||||
ecs::query::Without,
|
entity::metadata::{AirSupply, Score},
|
||||||
entity::{
|
|
||||||
Dead, EntityKind, EntityUuid, Position as AzaleaPosition,
|
|
||||||
metadata::{AirSupply, CustomName, Score},
|
|
||||||
},
|
|
||||||
world::MinecraftEntityId,
|
|
||||||
};
|
};
|
||||||
use mlua::{Function, Lua, Result, UserData, UserDataFields, UserDataMethods};
|
use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods};
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub inner: Option<AzaleaClient>,
|
pub inner: Option<AzaleaClient>,
|
||||||
@ -87,7 +86,11 @@ 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("set_client_information", state::set_client_information);
|
m.add_async_method("set_client_information", state::set_client_information);
|
||||||
m.add_method("chat", chat);
|
m.add_method("chat", chat);
|
||||||
m.add_method("find_entities", find_entities);
|
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("stop_pathfinding", movement::stop_pathfinding);
|
m.add_method("stop_pathfinding", movement::stop_pathfinding);
|
||||||
m.add_method_mut("attack", interaction::attack);
|
m.add_method_mut("attack", interaction::attack);
|
||||||
m.add_method_mut("block_interact", interaction::block_interact);
|
m.add_method_mut("block_interact", interaction::block_interact);
|
||||||
@ -102,39 +105,6 @@ impl UserData for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.inner.as_ref().unwrap().chat(&message);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
132
src/scripting/client/world.rs
Normal file
132
src/scripting/client/world.rs
Normal 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,
|
||||||
|
}))
|
||||||
|
}
|
36
src/scripting/fluid_state.rs
Normal file
36
src/scripting/fluid_state.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
pub mod block;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod direction;
|
pub mod direction;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
|
pub mod fluid_state;
|
||||||
pub mod hunger;
|
pub mod hunger;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod vec3;
|
pub mod vec3;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user