Compare commits

..

No commits in common. "833835d8cb029d3e1956ab277f06e751d9254386" and "bbb026b7bc3052786d0bce3ceb6e2a815b1c9998" have entirely different histories.

13 changed files with 112 additions and 97 deletions

View File

@ -1,7 +1,3 @@
[target.x86_64-unknown-linux-gnu] [target.x86_64-unknown-linux-gnu]
linker = "clang" linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=mold", "-Zshare-generics=y"] rustflags = ["-Clink-arg=-fuse-ld=mold", "-Zshare-generics=y"]
[target.aarch64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=mold", "-Zshare-generics=y"]

View File

@ -3,7 +3,6 @@ on:
push: push:
paths: paths:
- "**.rs" - "**.rs"
- "**.toml"
- "Cargo.*" - "Cargo.*"
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
@ -33,16 +32,10 @@ jobs:
~/.cargo/registry/cache/ ~/.cargo/registry/cache/
~/.cargo/git/db/ ~/.cargo/git/db/
target/ target/
key: build-${{ matrix.os }}-${{ hashFiles('**.toml', 'Cargo.*') }} key: build-${{ matrix.os }}-${{ hashFiles('Cargo.*') }}
- name: Switch to nightly toolchain - name: Switch to nightly toolchain
run: rustup default nightly run: rustup default nightly
- name: Build in release mode - name: Build in release mode
run: cargo build --release run: cargo build --release
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: errornowatcher_${{ matrix.os }}
path: target/release/errornowatcher

View File

@ -3,7 +3,6 @@ on:
push: push:
paths: paths:
- "**.rs" - "**.rs"
- "**.toml"
- "Cargo.*" - "Cargo.*"
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
@ -47,7 +46,7 @@ jobs:
~/.cargo/registry/cache/ ~/.cargo/registry/cache/
~/.cargo/git/db/ ~/.cargo/git/db/
target/ target/
key: build-${{ matrix.os }}-${{ hashFiles('**.toml', 'Cargo.*') }} key: build-${{ runner.os }}-${{ hashFiles('Cargo.*') }}
- name: Switch to nightly toolchain - name: Switch to nightly toolchain
run: rustup default nightly run: rustup default nightly

View File

@ -175,10 +175,11 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> Result<
_ => (), _ => (),
}, },
Event::Init => { Event::Init => {
debug!("received init event"); debug!("received initialize event");
let ecs = client.ecs.clone(); let ecs = client.ecs.clone();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
debug!("finishing replay recording");
ecs.lock() ecs.lock()
.remove_resource::<Recorder>() .remove_resource::<Recorder>()
.map(Recorder::finish); .map(Recorder::finish);
@ -240,7 +241,12 @@ async fn lua_init(client: Client, state: &State, globals: &Table) -> Result<()>
.map_err(Error::external) .map_err(Error::external)
})?, })?,
)?; )?;
globals.set("client", client::Client(Some(client)))?; globals.set(
"client",
client::Client {
inner: Some(client),
},
)?;
call_listeners(state, "init", ()).await; call_listeners(state, "init", ()).await;
Ok(()) Ok(())
} }

View File

@ -3,45 +3,44 @@ use crate::{
lua::{eval, exec, reload}, lua::{eval, exec, reload},
}; };
use http_body_util::{BodyExt, Empty, Full, combinators::BoxBody}; use http_body_util::{BodyExt, Empty, Full, combinators::BoxBody};
use hyper::{ use hyper::{Method, Request, Response, StatusCode, body::Bytes};
Error, Method, Request, Response, StatusCode,
body::{Bytes, Incoming},
};
pub async fn serve( pub async fn serve(
request: Request<Incoming>, request: Request<hyper::body::Incoming>,
state: State, state: State,
) -> Result<Response<BoxBody<Bytes, Error>>, Error> { ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
macro_rules! handle_code { let path = request.uri().path().to_owned();
($handler:ident) => {
match std::str::from_utf8(&request.into_body().collect().await?.to_bytes()) { Ok(match (request.method(), path.as_str()) {
Ok(code) => Response::new(full(format!( (&Method::POST, "/reload") => {
"{:#?}", Response::new(full(format!("{:#?}", reload(&state.lua, None))))
$handler(&state.lua, code, None).await }
))),
(&Method::POST, "/eval" | "/exec") => {
let bytes = request.into_body().collect().await?.to_bytes();
match std::str::from_utf8(&bytes) {
Ok(code) => Response::new(full(match path.as_str() {
"/eval" => format!("{:#?}", eval(&state.lua, code, None).await),
"/exec" => format!("{:#?}", exec(&state.lua, code, None).await),
_ => unreachable!(),
})),
Err(error) => status_code_response( Err(error) => status_code_response(
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
full(format!("invalid utf-8 data received: {error:?}")), full(format!("invalid utf-8 data received: {error:?}")),
), ),
} }
};
} }
Ok(match (request.method(), request.uri().path()) {
(&Method::POST, "/reload") => {
Response::new(full(format!("{:#?}", reload(&state.lua, None))))
}
(&Method::POST, "/eval") => handle_code!(eval),
(&Method::POST, "/exec") => handle_code!(exec),
(&Method::GET, "/ping") => Response::new(full("pong!")), (&Method::GET, "/ping") => Response::new(full("pong!")),
_ => status_code_response(StatusCode::NOT_FOUND, empty()), _ => status_code_response(StatusCode::NOT_FOUND, empty()),
}) })
} }
fn status_code_response( fn status_code_response(
status_code: StatusCode, status_code: StatusCode,
bytes: BoxBody<Bytes, Error>, bytes: BoxBody<Bytes, hyper::Error>,
) -> Response<BoxBody<Bytes, Error>> { ) -> Response<BoxBody<Bytes, hyper::Error>> {
let mut response = Response::new(bytes); let mut response = Response::new(bytes);
*response.status_mut() = status_code; *response.status_mut() = status_code;
response response

View File

@ -9,11 +9,13 @@ use log::error;
use mlua::{Lua, Result, Table, UserDataRef}; use mlua::{Lua, Result, Table, UserDataRef};
pub fn container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> { pub fn container(_lua: &Lua, client: &Client) -> Result<Option<ContainerRef>> {
Ok(client.get_open_container().map(ContainerRef)) Ok(client
.get_open_container()
.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(client.component::<Inventory>().held_item())) Ok(ItemStack::from(client.component::<Inventory>().held_item()))
} }
pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> { pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> {
@ -24,7 +26,7 @@ pub fn held_slot(_lua: &Lua, client: &Client) -> Result<u8> {
pub fn menu(lua: &Lua, client: &Client) -> Result<Table> { pub fn menu(lua: &Lua, client: &Client) -> Result<Table> {
fn from_slot_list<const N: usize>(s: SlotList<N>) -> Vec<ItemStack> { fn from_slot_list<const N: usize>(s: SlotList<N>) -> Vec<ItemStack> {
s.iter() s.iter()
.map(|i| ItemStack(i.to_owned())) .map(|i| ItemStack::from(i.to_owned()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
@ -38,11 +40,11 @@ pub fn menu(lua: &Lua, client: &Client) -> Result<Table> {
offhand, offhand,
}) => { }) => {
table.set("type", 0)?; table.set("type", 0)?;
table.set("craft_result", ItemStack(craft_result))?; table.set("craft_result", ItemStack::from(craft_result))?;
table.set("craft", from_slot_list(craft))?; table.set("craft", from_slot_list(craft))?;
table.set("armor", from_slot_list(armor))?; table.set("armor", from_slot_list(armor))?;
table.set("inventory", from_slot_list(inventory))?; table.set("inventory", from_slot_list(inventory))?;
table.set("offhand", ItemStack(offhand))?; table.set("offhand", ItemStack::from(offhand))?;
} }
Menu::Generic9x3 { contents, player } => { Menu::Generic9x3 { contents, player } => {
table.set("type", 3)?; table.set("type", 3)?;
@ -60,7 +62,7 @@ pub fn menu(lua: &Lua, client: &Client) -> Result<Table> {
player, player,
} => { } => {
table.set("type", 13)?; table.set("type", 13)?;
table.set("result", ItemStack(result))?; table.set("result", ItemStack::from(result))?;
table.set("grid", from_slot_list(grid))?; table.set("grid", from_slot_list(grid))?;
table.set("player", from_slot_list(player))?; table.set("player", from_slot_list(player))?;
} }
@ -76,7 +78,7 @@ pub fn menu(lua: &Lua, client: &Client) -> Result<Table> {
} => { } => {
table.set("type", 20)?; table.set("type", 20)?;
table.set("payments", from_slot_list(payments))?; table.set("payments", from_slot_list(payments))?;
table.set("result", ItemStack(result))?; table.set("result", ItemStack::from(result))?;
table.set("player", from_slot_list(player))?; table.set("player", from_slot_list(player))?;
} }
Menu::ShulkerBox { contents, player } => { Menu::ShulkerBox { contents, player } => {
@ -103,11 +105,11 @@ pub async fn open_container_at(
position.z as i32, position.z as i32,
)) ))
.await .await
.map(Container)) .map(|c| Container { inner: c }))
} }
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.open_inventory().map(Container)) Ok(client.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<()> {

View File

@ -14,19 +14,25 @@ use azalea::{Client as AzaleaClient, world::MinecraftEntityId};
use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods}; use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub struct Client(pub Option<AzaleaClient>); pub struct Client {
pub inner: Option<AzaleaClient>,
}
impl Deref for Client { impl Deref for Client {
type Target = AzaleaClient; type Target = AzaleaClient;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.0.as_ref().expect("should have received init event") self.inner
.as_ref()
.expect("should have received init event")
} }
} }
impl DerefMut for Client { impl DerefMut for Client {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().expect("should have received init event") self.inner
.as_mut()
.expect("should have received init event")
} }
} }

View File

@ -1,19 +1,24 @@
use azalea::inventory::{ use azalea::inventory::components::{CustomName, Damage, Food, MaxDamage};
self,
components::{CustomName, Damage, Food, MaxDamage},
};
use mlua::{UserData, UserDataFields, UserDataMethods}; use mlua::{UserData, UserDataFields, UserDataMethods};
pub struct ItemStack(pub inventory::ItemStack); pub struct ItemStack {
pub inner: azalea::inventory::ItemStack,
}
impl From<azalea::inventory::ItemStack> for ItemStack {
fn from(inner: azalea::inventory::ItemStack) -> Self {
Self { inner }
}
}
impl UserData for ItemStack { impl UserData for ItemStack {
fn add_fields<F: UserDataFields<Self>>(f: &mut F) { fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("is_empty", |_, this| Ok(this.0.is_empty())); f.add_field_method_get("is_empty", |_, this| Ok(this.inner.is_empty()));
f.add_field_method_get("is_present", |_, this| Ok(this.0.is_present())); f.add_field_method_get("is_present", |_, this| Ok(this.inner.is_present()));
f.add_field_method_get("count", |_, this| Ok(this.0.count())); f.add_field_method_get("count", |_, this| Ok(this.inner.count()));
f.add_field_method_get("kind", |_, this| Ok(this.0.kind().to_string())); f.add_field_method_get("kind", |_, this| Ok(this.inner.kind().to_string()));
f.add_field_method_get("custom_name", |_, this| { f.add_field_method_get("custom_name", |_, this| {
Ok(this.0.as_present().map(|data| { Ok(this.inner.as_present().map(|data| {
data.components data.components
.get::<CustomName>() .get::<CustomName>()
.map(|c| c.name.to_string()) .map(|c| c.name.to_string())
@ -21,13 +26,13 @@ impl UserData for ItemStack {
}); });
f.add_field_method_get("damage", |_, this| { f.add_field_method_get("damage", |_, this| {
Ok(this Ok(this
.0 .inner
.as_present() .as_present()
.map(|data| data.components.get::<Damage>().map(|d| d.amount))) .map(|data| data.components.get::<Damage>().map(|d| d.amount)))
}); });
f.add_field_method_get("max_damage", |_, this| { f.add_field_method_get("max_damage", |_, this| {
Ok(this Ok(this
.0 .inner
.as_present() .as_present()
.map(|data| data.components.get::<MaxDamage>().map(|d| d.amount))) .map(|data| data.components.get::<MaxDamage>().map(|d| d.amount)))
}); });
@ -35,7 +40,7 @@ impl UserData for ItemStack {
f.add_field_method_get("food", |lua, this| { f.add_field_method_get("food", |lua, this| {
Ok( Ok(
if let Some(food) = this if let Some(food) = this
.0 .inner
.as_present() .as_present()
.and_then(|data| data.components.get::<Food>()) .and_then(|data| data.components.get::<Food>())
{ {
@ -54,10 +59,10 @@ impl UserData for ItemStack {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) { fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_method_mut("split", |_, this, count: u32| { m.add_method_mut("split", |_, this, count: u32| {
Ok(ItemStack(this.0.split(count))) Ok(ItemStack::from(this.inner.split(count)))
}); });
m.add_method_mut("update_empty", |_, this, (): ()| { m.add_method_mut("update_empty", |_, this, (): ()| {
this.0.update_empty(); this.inner.update_empty();
Ok(()) Ok(())
}); });
} }

View File

@ -6,20 +6,22 @@ use click::operation_from_table;
use item_stack::ItemStack; use item_stack::ItemStack;
use mlua::{Table, UserData, UserDataFields, UserDataMethods}; use mlua::{Table, UserData, UserDataFields, UserDataMethods};
pub struct Container(pub ContainerHandle); pub struct Container {
pub inner: ContainerHandle,
}
impl UserData for Container { impl UserData for Container {
fn add_fields<F: UserDataFields<Self>>(f: &mut F) { fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("id", |_, this| Ok(this.0.id())); f.add_field_method_get("id", |_, this| Ok(this.inner.id()));
f.add_field_method_get("menu", |_, this| { f.add_field_method_get("menu", |_, this| {
Ok(this.0.menu().map(|m| format!("{m:?}"))) Ok(this.inner.menu().map(|m| format!("{m:?}")))
}); });
f.add_field_method_get("contents", |_, this| { f.add_field_method_get("contents", |_, this| {
Ok(this.0.contents().map(|v| { Ok(this.inner.contents().map(|v| {
v.iter() v.iter()
.map(|i| ItemStack(i.to_owned())) .map(|i| ItemStack::from(i.to_owned()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
})) }))
}); });
@ -29,7 +31,7 @@ impl UserData for Container {
m.add_method( m.add_method(
"click", "click",
|_, this, (operation, operation_type): (Table, Option<u8>)| { |_, this, (operation, operation_type): (Table, Option<u8>)| {
this.0 this.inner
.click(operation_from_table(operation, operation_type)?); .click(operation_from_table(operation, operation_type)?);
Ok(()) Ok(())
}, },
@ -37,20 +39,22 @@ impl UserData for Container {
} }
} }
pub struct ContainerRef(pub ContainerHandleRef); pub struct ContainerRef {
pub inner: ContainerHandleRef,
}
impl UserData for ContainerRef { impl UserData for ContainerRef {
fn add_fields<F: UserDataFields<Self>>(f: &mut F) { fn add_fields<F: UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("id", |_, this| Ok(this.0.id())); f.add_field_method_get("id", |_, this| Ok(this.inner.id()));
f.add_field_method_get("menu", |_, this| { f.add_field_method_get("menu", |_, this| {
Ok(this.0.menu().map(|m| format!("{m:?}"))) Ok(this.inner.menu().map(|m| format!("{m:?}")))
}); });
f.add_field_method_get("contents", |_, this| { f.add_field_method_get("contents", |_, this| {
Ok(this.0.contents().map(|v| { Ok(this.inner.contents().map(|v| {
v.iter() v.iter()
.map(|i| ItemStack(i.to_owned())) .map(|i| ItemStack::from(i.to_owned()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
})) }))
}); });
@ -58,14 +62,14 @@ impl UserData for ContainerRef {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) { fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_method("close", |_, this, (): ()| { m.add_method("close", |_, this, (): ()| {
this.0.close(); this.inner.close();
Ok(()) Ok(())
}); });
m.add_method( m.add_method(
"click", "click",
|_, this, (operation, operation_type): (Table, Option<u8>)| { |_, this, (operation, operation_type): (Table, Option<u8>)| {
this.0 this.inner
.click(operation_from_table(operation, operation_type)?); .click(operation_from_table(operation, operation_type)?);
Ok(()) Ok(())
}, },

View File

@ -4,7 +4,7 @@ macro_rules! crypt {
macro_rules! crypt_with { macro_rules! crypt_with {
($algo:ident) => {{ ($algo:ident) => {{
let encoding = $options.get("encoding").unwrap_or_default(); let encoding = $options.get("encoding").unwrap_or_default();
let key = &$options.get::<UserDataRef<AesKey>>("key")?.0; let key = &$options.get::<UserDataRef<AesKey>>("key")?.inner;
match encoding { match encoding {
1 => $algo::<Base64Encoding>::$op($text, &key), 1 => $algo::<Base64Encoding>::$op($text, &key),
2 => $algo::<Base64rEncoding>::$op($text, &key), 2 => $algo::<Base64rEncoding>::$op($text, &key),

View File

@ -1,10 +1,12 @@
use mlua::UserData; use mlua::UserData;
pub struct AesKey(pub ncr::AesKey); pub struct AesKey {
pub inner: ncr::AesKey,
}
impl UserData for AesKey { impl UserData for AesKey {
fn add_fields<F: mlua::UserDataFields<Self>>(f: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("base64", |_, this| Ok(this.0.encode_base64())); f.add_field_method_get("base64", |_, this| Ok(this.inner.encode_base64()));
f.add_field_method_get("bytes", |_, this| Ok(this.0.as_ref().to_vec())); f.add_field_method_get("bytes", |_, this| Ok(this.inner.as_ref().to_vec()));
} }
} }

View File

@ -14,23 +14,29 @@ pub fn register_globals(lua: &Lua, globals: &Table) -> Result<()> {
globals.set( globals.set(
"ncr_aes_key_from_passphrase", "ncr_aes_key_from_passphrase",
lua.create_function(|_, passphrase: Vec<u8>| { lua.create_function(|_, passphrase: Vec<u8>| {
Ok(AesKey(ncr::AesKey::gen_from_passphrase(&passphrase))) Ok(AesKey {
inner: ncr::AesKey::gen_from_passphrase(&passphrase),
})
})?, })?,
)?; )?;
globals.set( globals.set(
"ncr_aes_key_from_base64", "ncr_aes_key_from_base64",
lua.create_function(|_, base64: String| { lua.create_function(|_, base64: String| {
Ok(AesKey( Ok(AesKey {
ncr::AesKey::decode_base64(&base64) inner: ncr::AesKey::decode_base64(&base64)
.map_err(|error| Error::external(error.to_string()))?, .map_err(|error| Error::external(error.to_string()))?,
)) })
})?, })?,
)?; )?;
globals.set( globals.set(
"ncr_generate_random_aes_key", "ncr_generate_random_aes_key",
lua.create_function(|_, (): ()| Ok(AesKey(ncr::AesKey::gen_random_key())))?, lua.create_function(|_, (): ()| {
Ok(AesKey {
inner: ncr::AesKey::gen_random_key(),
})
})?,
)?; )?;
globals.set( globals.set(

View File

@ -5,7 +5,6 @@ use azalea::{
prelude::Resource, prelude::Resource,
protocol::packets::{PROTOCOL_VERSION, ProtocolPacket, VERSION_NAME}, protocol::packets::{PROTOCOL_VERSION, ProtocolPacket, VERSION_NAME},
}; };
use log::debug;
use serde_json::json; use serde_json::json;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
@ -42,9 +41,8 @@ impl Recorder {
} }
pub fn finish(mut self) -> Result<()> { pub fn finish(mut self) -> Result<()> {
debug!("finishing replay recording");
let elapsed = self.start.elapsed().as_millis(); let elapsed = self.start.elapsed().as_millis();
self.zip_writer self.zip_writer
.start_file("metaData.json", SimpleFileOptions::default())?; .start_file("metaData.json", SimpleFileOptions::default())?;
self.zip_writer.write_all( self.zip_writer.write_all(
@ -64,21 +62,20 @@ impl Recorder {
)?; )?;
self.zip_writer.finish()?; self.zip_writer.finish()?;
debug!("finished replay recording");
Ok(()) Ok(())
} }
pub fn save_raw_packet(&mut self, raw_packet: &[u8]) -> Result<()> { pub fn save_raw_packet(&mut self, raw_packet: &[u8]) -> Result<()> {
let mut data = SmallVec::<[u8; 128]>::with_capacity(raw_packet.len() + 8); let mut data = Vec::with_capacity(raw_packet.len() + 8);
data.write_all(&TryInto::<u32>::try_into(self.start.elapsed().as_millis())?.to_be_bytes())?; data.extend(&TryInto::<u32>::try_into(self.start.elapsed().as_millis())?.to_be_bytes());
data.write_all(&TryInto::<u32>::try_into(raw_packet.len())?.to_be_bytes())?; data.extend(&TryInto::<u32>::try_into(raw_packet.len())?.to_be_bytes());
data.write_all(raw_packet)?; data.extend(raw_packet);
self.zip_writer.write_all(&data)?; self.zip_writer.write_all(&data)?;
Ok(()) Ok(())
} }
pub fn save_packet<T: ProtocolPacket>(&mut self, packet: &T) -> Result<()> { pub fn save_packet<T: ProtocolPacket>(&mut self, packet: &T) -> Result<()> {
let mut raw_packet = SmallVec::<[u8; 128]>::new(); let mut raw_packet = SmallVec::<[u8; 64]>::new();
packet.id().azalea_write_var(&mut raw_packet)?; packet.id().azalea_write_var(&mut raw_packet)?;
packet.write(&mut raw_packet)?; packet.write(&mut raw_packet)?;
self.save_raw_packet(&raw_packet) self.save_raw_packet(&raw_packet)