refactor: clean up and restructure
This commit is contained in:
parent
c4702a74f2
commit
645483c98f
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1179,7 +1179,6 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"azalea",
|
"azalea",
|
||||||
"clap",
|
"clap",
|
||||||
"futures",
|
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
|
@ -19,7 +19,6 @@ strip = true
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
azalea = { git = "https://github.com/azalea-rs/azalea.git" }
|
azalea = { git = "https://github.com/azalea-rs/azalea.git" }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
futures = "0"
|
|
||||||
http-body-util = "0"
|
http-body-util = "0"
|
||||||
hyper = { version = "1", features = ["server"] }
|
hyper = { version = "1", features = ["server"] }
|
||||||
hyper-util = "0"
|
hyper-util = "0"
|
||||||
|
@ -9,6 +9,6 @@ pub struct Arguments {
|
|||||||
pub script: Option<PathBuf>,
|
pub script: Option<PathBuf>,
|
||||||
|
|
||||||
/// Socket address to bind HTTP server to
|
/// Socket address to bind HTTP server to
|
||||||
#[arg(short, long)]
|
#[arg(short = 'a', long)]
|
||||||
pub address: Option<SocketAddr>,
|
pub http_address: Option<SocketAddr>,
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,14 @@ pub struct CommandSource {
|
|||||||
|
|
||||||
impl CommandSource {
|
impl CommandSource {
|
||||||
pub fn reply(&self, message: &str) {
|
pub fn reply(&self, message: &str) {
|
||||||
if self.message.is_whisper()
|
let response = if self.message.is_whisper()
|
||||||
&& let Some(username) = self.message.username()
|
&& let Some(username) = self.message.username()
|
||||||
{
|
{
|
||||||
self.client.chat(&format!("/w {username} {message}"));
|
&format!("/w {username} {message}")
|
||||||
} else {
|
} else {
|
||||||
self.client.chat(message);
|
message
|
||||||
}
|
};
|
||||||
|
self.client.chat(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _entity(&mut self) -> Option<Entity> {
|
pub fn _entity(&mut self) -> Option<Entity> {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{State, commands::CommandSource, http::handle, scripting};
|
use crate::{State, commands::CommandSource, http::serve, scripting};
|
||||||
use azalea::prelude::*;
|
use azalea::prelude::*;
|
||||||
use hyper::{server::conn::http1, service::service_fn};
|
use hyper::{server::conn::http1, service::service_fn};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use log::{error, info};
|
|
||||||
use mlua::Function;
|
use mlua::Function;
|
||||||
|
use log::{debug, error, info, trace};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
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<()> {
|
||||||
@ -33,38 +33,54 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
|
|||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
.reply(&format!("{error:?}"));
|
.reply(&format!("{error:?}"));
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Init => {
|
Event::Init => {
|
||||||
|
debug!("client initialized");
|
||||||
|
|
||||||
globals.set(
|
globals.set(
|
||||||
"client",
|
"client",
|
||||||
scripting::client::Client {
|
scripting::client::Client {
|
||||||
inner: Some(client),
|
inner: Some(client),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
globals.get::<Function>("Init")?.call::<()>(())?;
|
if let Ok(on_init) = globals.get::<Function>("on_init")
|
||||||
|
&& let Err(error) = on_init.call::<()>(())
|
||||||
|
{
|
||||||
|
error!("failed to call lua on_init function: {error:?}");
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(address) = state.http_address {
|
||||||
|
let listener = TcpListener::bind(address).await.map_err(|error| {
|
||||||
|
error!("failed to listen on {address}: {error:?}");
|
||||||
|
error
|
||||||
|
})?;
|
||||||
|
debug!("http server listening on {address}");
|
||||||
|
|
||||||
if let Some(address) = state.address {
|
|
||||||
let listener = TcpListener::bind(address).await?;
|
|
||||||
loop {
|
loop {
|
||||||
let (stream, _) = listener.accept().await?;
|
let (stream, peer) = listener.accept().await?;
|
||||||
let io = TokioIo::new(stream);
|
trace!("http server got connection from {peer}");
|
||||||
|
|
||||||
let state = state.clone();
|
let conn_state = state.clone();
|
||||||
let service = service_fn(move |request| {
|
let service = service_fn(move |request| {
|
||||||
let state = state.clone();
|
let request_state = conn_state.clone();
|
||||||
async move { handle(request, state).await }
|
async move { serve(request, request_state).await }
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(error) = http1::Builder::new().serve_connection(io, service).await {
|
tokio::task::spawn(async move {
|
||||||
error!("failed to serve connection: {error:?}");
|
if let Err(error) = http1::Builder::new()
|
||||||
}
|
.serve_connection(TokioIo::new(stream), service)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("failed to serve connection: {error:?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
34
src/http.rs
34
src/http.rs
@ -5,24 +5,24 @@ use crate::{
|
|||||||
use http_body_util::{BodyExt, Empty, Full, combinators::BoxBody};
|
use http_body_util::{BodyExt, Empty, Full, combinators::BoxBody};
|
||||||
use hyper::{Method, Request, Response, StatusCode, body::Bytes};
|
use hyper::{Method, Request, Response, StatusCode, body::Bytes};
|
||||||
|
|
||||||
pub async fn handle(
|
pub async fn serve(
|
||||||
request: Request<hyper::body::Incoming>,
|
request: Request<hyper::body::Incoming>,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
||||||
let path = request.uri().path().to_owned();
|
let path = request.uri().path().to_owned();
|
||||||
|
|
||||||
match (request.method(), path.as_str()) {
|
Ok(match (request.method(), path.as_str()) {
|
||||||
(&Method::POST, "/reload") => Ok(match reload(&state.lua.lock()) {
|
(&Method::POST, "/reload") => match reload(&state.lua.lock()) {
|
||||||
Ok(()) => Response::new(empty()),
|
Ok(()) => Response::new(empty()),
|
||||||
Err(error) => status_code_response(
|
Err(error) => status_code_response(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Some(full(format!("{error:?}"))),
|
full(format!("{error:?}")),
|
||||||
),
|
),
|
||||||
}),
|
},
|
||||||
|
|
||||||
(&Method::POST, "/eval" | "/exec") => {
|
(&Method::POST, "/eval" | "/exec") => {
|
||||||
let bytes = request.into_body().collect().await?.to_bytes();
|
let bytes = request.into_body().collect().await?.to_bytes();
|
||||||
Ok(match std::str::from_utf8(&bytes) {
|
match std::str::from_utf8(&bytes) {
|
||||||
Ok(code) => {
|
Ok(code) => {
|
||||||
let lua = state.lua.lock();
|
let lua = state.lua.lock();
|
||||||
Response::new(full(match path.as_str() {
|
Response::new(full(match path.as_str() {
|
||||||
@ -31,26 +31,24 @@ pub async fn handle(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => status_code_response(
|
||||||
return Ok(status_code_response(
|
StatusCode::BAD_REQUEST,
|
||||||
StatusCode::BAD_REQUEST,
|
full(format!("invalid utf-8 data received: {error:?}")),
|
||||||
Some(full(format!("invalid utf-8 data received: {error:?}"))),
|
),
|
||||||
));
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(&Method::GET, "/ping") => Ok(Response::new(full("pong!"))),
|
(&Method::GET, "/ping") => Response::new(full("pong!")),
|
||||||
|
|
||||||
_ => Ok(status_code_response(StatusCode::NOT_FOUND, None)),
|
_ => status_code_response(StatusCode::NOT_FOUND, empty()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status_code_response(
|
fn status_code_response(
|
||||||
status_code: StatusCode,
|
status_code: StatusCode,
|
||||||
bytes: Option<BoxBody<Bytes, hyper::Error>>,
|
bytes: BoxBody<Bytes, hyper::Error>,
|
||||||
) -> Response<BoxBody<Bytes, hyper::Error>> {
|
) -> Response<BoxBody<Bytes, hyper::Error>> {
|
||||||
let mut response = Response::new(bytes.unwrap_or(empty()));
|
let mut response = Response::new(bytes);
|
||||||
*response.status_mut() = status_code;
|
*response.status_mut() = status_code;
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
66
src/main.rs
66
src/main.rs
@ -12,62 +12,29 @@ use commands::{CommandSource, register};
|
|||||||
use events::handle_event;
|
use events::handle_event;
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{net::SocketAddr, path::PathBuf, process::ExitCode, sync::Arc};
|
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
#[derive(Default, Clone, Component)]
|
#[derive(Default, Clone, Component)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
|
commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
|
||||||
lua: Arc<Mutex<Lua>>,
|
lua: Arc<Mutex<Lua>>,
|
||||||
address: Option<SocketAddr>,
|
http_address: Option<SocketAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> ExitCode {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let args = arguments::Arguments::parse();
|
let args = arguments::Arguments::parse();
|
||||||
let lua = Lua::new();
|
let script_path = args.script.unwrap_or(PathBuf::from("errornowatcher.lua"));
|
||||||
|
|
||||||
let config_path = args.script.unwrap_or(PathBuf::from("errornowatcher.lua"));
|
let lua = Lua::new();
|
||||||
if let Err(error) = match &std::fs::read_to_string(&config_path) {
|
lua.load(std::fs::read_to_string(&script_path)?).exec()?;
|
||||||
Ok(string) => lua.load(string).exec(),
|
|
||||||
Err(error) => {
|
|
||||||
eprintln!("failed to read {config_path:?}: {error:?}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
eprintln!("failed to execute configuration as lua code: {error:?}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
let Ok(server) = globals.get::<String>("SERVER") else {
|
let server = globals.get::<String>("SERVER")?;
|
||||||
eprintln!("no server defined in lua globals!");
|
let username = globals.get::<String>("USERNAME")?;
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
let Ok(username) = globals.get::<String>("USERNAME") else {
|
|
||||||
eprintln!("no username defined in lua globals!");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(error) = globals.set("config_path", config_path) {
|
globals.set("script_path", script_path)?;
|
||||||
eprintln!("failed to set config_path in lua globals: {error:?}");
|
scripting::logging::register(&lua, &globals)?;
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
if let Err(error) = scripting::logging::init(&lua, &globals) {
|
|
||||||
eprintln!("failed to set up logging wrappers: {error:?}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
|
|
||||||
let account = if username.contains('@') {
|
|
||||||
match Account::microsoft(&username).await {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(error) => {
|
|
||||||
eprintln!("failed to login using microsoft account: {error:?}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Account::offline(&username)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut commands = CommandDispatcher::new();
|
let mut commands = CommandDispatcher::new();
|
||||||
register(&mut commands);
|
register(&mut commands);
|
||||||
@ -77,11 +44,18 @@ async fn main() -> ExitCode {
|
|||||||
.set_state(State {
|
.set_state(State {
|
||||||
commands: Arc::new(commands),
|
commands: Arc::new(commands),
|
||||||
lua: Arc::new(Mutex::new(lua)),
|
lua: Arc::new(Mutex::new(lua)),
|
||||||
address: args.address,
|
http_address: args.http_address,
|
||||||
})
|
})
|
||||||
.start(account, server.as_ref())
|
.start(
|
||||||
|
if username.contains('@') {
|
||||||
|
Account::microsoft(&username).await?
|
||||||
|
} else {
|
||||||
|
Account::offline(&username)
|
||||||
|
},
|
||||||
|
server.as_ref(),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
eprintln!("{error:?}");
|
eprintln!("{error:?}");
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
use super::position::{from_table, to_table};
|
|
||||||
use azalea::{
|
|
||||||
BlockPos, Client as AzaleaClient, ClientInformation,
|
|
||||||
ecs::query::Without,
|
|
||||||
entity::{Dead, EntityKind, EntityUuid, Position, metadata::CustomName},
|
|
||||||
pathfinder::goals::BlockPosGoal,
|
|
||||||
prelude::PathfinderClientExt,
|
|
||||||
world::MinecraftEntityId,
|
|
||||||
};
|
|
||||||
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataRef};
|
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
pub inner: Option<AzaleaClient>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserData for Client {
|
|
||||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
|
||||||
fields.add_field_method_get("pos", |lua, this| {
|
|
||||||
let pos = this.inner.as_ref().unwrap().position();
|
|
||||||
to_table(lua, pos.x, pos.y, pos.z)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_method("set_client_information", set_client_information);
|
|
||||||
methods.add_method("get_entity", get_entity);
|
|
||||||
methods.add_method_mut("get_entity_position", get_entity_position);
|
|
||||||
methods.add_method_mut("goto", goto);
|
|
||||||
methods.add_method_mut("stop", stop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_entity(lua: &Lua, client: &Client, filter_fn: Function) -> Result<u32> {
|
|
||||||
let mut ecs = client.inner.as_ref().unwrap().ecs.lock();
|
|
||||||
let mut query = ecs.query_filtered::<(
|
|
||||||
&MinecraftEntityId,
|
|
||||||
&EntityUuid,
|
|
||||||
&EntityKind,
|
|
||||||
&Position,
|
|
||||||
&CustomName,
|
|
||||||
), Without<Dead>>();
|
|
||||||
|
|
||||||
for (&entity_id, entity_uuid, entity_kind, pos, custom_name) in query.iter(&ecs) {
|
|
||||||
let entity = lua.create_table()?;
|
|
||||||
|
|
||||||
entity.set("id", entity_id.0)?;
|
|
||||||
entity.set("uuid", entity_uuid.to_string())?;
|
|
||||||
entity.set("kind", entity_kind.0.to_string())?;
|
|
||||||
entity.set("pos", to_table(lua, pos.x, pos.y, pos.z)?)?;
|
|
||||||
if let Some(n) = &**custom_name {
|
|
||||||
entity.set("custom_name", n.to_string())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter_fn.call::<bool>(entity).unwrap() {
|
|
||||||
return Ok(entity_id.0);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::RuntimeError("entity not found".to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_entity_position(lua: &Lua, client: &mut Client, entity_id: u32) -> Result<Table> {
|
|
||||||
let client = client.inner.as_mut().unwrap();
|
|
||||||
let entity = client
|
|
||||||
.entity_by::<Without<Dead>, &MinecraftEntityId>(|query_entity_id: &&MinecraftEntityId| {
|
|
||||||
query_entity_id.0 == entity_id
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
let pos = client.entity_component::<Position>(entity);
|
|
||||||
to_table(lua, pos.x, pos.y, pos.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(_lua: &Lua, client: &mut Client, pos_table: Table) -> Result<()> {
|
|
||||||
let pos = from_table(&pos_table)?;
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
|
||||||
client
|
|
||||||
.inner
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.goto(BlockPosGoal(BlockPos::new(
|
|
||||||
pos.0 as i32,
|
|
||||||
pos.1 as i32,
|
|
||||||
pos.2 as i32,
|
|
||||||
)));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop(_lua: &Lua, client: &mut Client, _: ()) -> Result<()> {
|
|
||||||
client.inner.as_ref().unwrap().stop_pathfinding();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
68
src/scripting/client/mod.rs
Normal file
68
src/scripting/client/mod.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
mod pathfinding;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
use super::{entity::Entity, position::Position};
|
||||||
|
use azalea::{
|
||||||
|
Client as AzaleaClient,
|
||||||
|
ecs::query::Without,
|
||||||
|
entity::{Dead, EntityKind, EntityUuid, Position as AzaleaPosition, metadata::CustomName},
|
||||||
|
world::MinecraftEntityId,
|
||||||
|
};
|
||||||
|
use mlua::{Function, Lua, Result, UserData};
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
pub inner: Option<AzaleaClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Client {
|
||||||
|
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("position", |_, this| {
|
||||||
|
let position = this.inner.as_ref().unwrap().position();
|
||||||
|
Ok(Position {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
z: position.z,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("set_client_information", state::set_client_information);
|
||||||
|
methods.add_method("find_entities", find_entities);
|
||||||
|
methods.add_method("stop_pathfinding", pathfinding::stop_pathfinding);
|
||||||
|
methods.add_method_mut("goto", pathfinding::goto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: Position {
|
||||||
|
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)
|
||||||
|
}
|
22
src/scripting/client/pathfinding.rs
Normal file
22
src/scripting/client/pathfinding.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use super::{Client, Position};
|
||||||
|
use azalea::{BlockPos, pathfinder::goals::BlockPosGoal, prelude::*};
|
||||||
|
use mlua::{Lua, Result};
|
||||||
|
|
||||||
|
pub fn goto(_lua: &Lua, client: &mut Client, position: Position) -> Result<()> {
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
client
|
||||||
|
.inner
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.goto(BlockPosGoal(BlockPos::new(
|
||||||
|
position.x as i32,
|
||||||
|
position.y as i32,
|
||||||
|
position.z as i32,
|
||||||
|
)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_pathfinding(_lua: &Lua, client: &Client, _: ()) -> Result<()> {
|
||||||
|
client.inner.as_ref().unwrap().stop_pathfinding();
|
||||||
|
Ok(())
|
||||||
|
}
|
21
src/scripting/client/state.rs
Normal file
21
src/scripting/client/state.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use super::Client;
|
||||||
|
use azalea::ClientInformation;
|
||||||
|
use mlua::{Lua, Result, Table, UserDataRef};
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
43
src/scripting/entity.rs
Normal file
43
src/scripting/entity.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use super::position::Position;
|
||||||
|
use mlua::{FromLua, IntoLua, Lua, Result, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Entity {
|
||||||
|
pub id: u32,
|
||||||
|
pub uuid: String,
|
||||||
|
pub kind: String,
|
||||||
|
pub position: Position,
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use mlua::{Lua, Result, Table};
|
use mlua::{Lua, Result, Table};
|
||||||
|
|
||||||
pub fn init(lua: &Lua, globals: &Table) -> Result<()> {
|
pub fn register(lua: &Lua, globals: &Table) -> Result<()> {
|
||||||
globals.set(
|
globals.set(
|
||||||
"error",
|
"error",
|
||||||
lua.create_function(|_, message: String| {
|
lua.create_function(|_, message: String| {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
|
pub mod entity;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
|
|
||||||
@ -7,19 +8,19 @@ use mlua::Lua;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
MissingGlobal(mlua::Error),
|
|
||||||
ReadFile(std::io::Error),
|
|
||||||
LoadChunk(mlua::Error),
|
|
||||||
EvalChunk(mlua::Error),
|
EvalChunk(mlua::Error),
|
||||||
ExecChunk(mlua::Error),
|
ExecChunk(mlua::Error),
|
||||||
|
LoadChunk(mlua::Error),
|
||||||
|
MissingPath(mlua::Error),
|
||||||
|
ReadFile(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(lua: &Lua) -> Result<(), Error> {
|
pub fn reload(lua: &Lua) -> Result<(), Error> {
|
||||||
lua.load(
|
lua.load(
|
||||||
&std::fs::read_to_string(
|
&std::fs::read_to_string(
|
||||||
lua.globals()
|
lua.globals()
|
||||||
.get::<String>("config_path")
|
.get::<String>("script_path")
|
||||||
.map_err(Error::MissingGlobal)?,
|
.map_err(Error::MissingPath)?,
|
||||||
)
|
)
|
||||||
.map_err(Error::ReadFile)?,
|
.map_err(Error::ReadFile)?,
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,36 @@
|
|||||||
use mlua::{Lua, Result, Table};
|
use mlua::{FromLua, IntoLua, Lua, Result, Value};
|
||||||
|
|
||||||
pub fn to_table(lua: &Lua, x: f64, y: f64, z: f64) -> Result<Table> {
|
#[derive(Clone)]
|
||||||
let table = lua.create_table()?;
|
pub struct Position {
|
||||||
table.set("x", x)?;
|
pub x: f64,
|
||||||
table.set("y", y)?;
|
pub y: f64,
|
||||||
table.set("z", z)?;
|
pub z: f64,
|
||||||
Ok(table)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_table(table: &Table) -> Result<(f64, f64, f64)> {
|
impl IntoLua for Position {
|
||||||
Ok((table.get("x")?, table.get("y")?, table.get("z")?))
|
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 Position {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user