Compare commits

..

No commits in common. "35ef5133e809d2e3153d4b6ef322c63b7d1649e9" and "caec5fa7f896b362b56108c7401c5694679baaba" have entirely different histories.

15 changed files with 39 additions and 76 deletions

21
Cargo.lock generated
View File

@ -1467,12 +1467,10 @@ dependencies = [
"hyper", "hyper",
"hyper-util", "hyper-util",
"log", "log",
"mimalloc",
"mlua", "mlua",
"ncr", "ncr",
"parking_lot", "parking_lot",
"serde_json", "serde_json",
"smallvec",
"tokio", "tokio",
"zip", "zip",
] ]
@ -2206,16 +2204,6 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libmimalloc-sys"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.21" version = "1.1.21"
@ -2312,15 +2300,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mimalloc"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
dependencies = [
"libmimalloc-sys",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"

View File

@ -32,15 +32,12 @@ http-body-util = "0"
hyper = { version = "1", features = ["server"] } hyper = { version = "1", features = ["server"] }
hyper-util = "0" hyper-util = "0"
log = { version = "0" } log = { version = "0" }
mimalloc = { version = "0", optional = true }
mlua = { version = "0", features = ["async", "luajit", "send"] } mlua = { version = "0", features = ["async", "luajit", "send"] }
ncr = { version = "0", features = ["cfb8", "ecb", "gcm"] } ncr = { version = "0", features = ["cfb8", "ecb", "gcm"] }
parking_lot = "0" parking_lot = "0"
serde_json = "1" serde_json = "1"
smallvec = { version = "1", features = ["write"] }
tokio = { version = "1", features = ["macros"] } tokio = { version = "1", features = ["macros"] }
zip = "2" zip = "2"
[features] [features]
console-subscriber = ["dep:console-subscriber"] console-subscriber = ["dep:console-subscriber"]
mimalloc = ["dep:mimalloc"]

View File

@ -1,3 +1,3 @@
fn main() { fn main() {
built::write_built_file().expect("appropriate environment variables should have been set"); built::write_built_file().unwrap();
} }

View File

@ -1,14 +1,13 @@
Server = "localhost" Server = "localhost"
Username = "ErrorNoWatcher" Username = "ErrorNoWatcher"
HttpAddress = "127.0.0.1:8080"
Owners = { "ErrorNoInternet" } Owners = { "ErrorNoInternet" }
for _, module in ipairs({ for _, module in ipairs({
"lib",
"automation", "automation",
"enum", "enum",
"events", "events",
"inventory", "inventory",
"lib",
"movement", "movement",
"utils", "utils",
}) do }) do

View File

@ -1,6 +1,6 @@
Center = { x = 0, y = 64, z = 0 } Center = { x = 0, y = 64, z = 0 }
Radius = 100 Radius = 100
Whitelist = table.shallow_copy(Owners) Whitelist = Owners
Ticks = -1 Ticks = -1
function check_radius() function check_radius()

View File

@ -145,7 +145,7 @@ function interact_bed()
return return
end end
client:go_to({ position = bed, radius = 2 }, { type = RADIUS_GOAL, options = { without_mining = true } }) client:go_to(bed, { type = REACH_BLOCK_POS_GOAL, options = { without_mining = true } })
while client.pathfinder.is_calculating or client.pathfinder.is_executing do while client.pathfinder.is_calculating or client.pathfinder.is_executing do
sleep(500) sleep(500)
end end

View File

@ -1,16 +1,16 @@
use crate::build_info; use crate::build_info;
use clap::Parser; use clap::Parser;
use std::path::PathBuf; use std::{net::SocketAddr, path::PathBuf};
/// A Minecraft utility bot /// A Minecraft utility bot
#[derive(Parser)] #[derive(Parser)]
#[command(version = build_info::version_formatted())] #[command(version = build_info::version_formatted())]
pub struct Arguments { pub struct Arguments {
/// Path to Lua entrypoint /// Path to main Lua file
#[arg(short, long)] #[arg(short, long)]
pub script: Option<PathBuf>, pub script: Option<PathBuf>,
/// Code to execute after loading script /// Socket address to bind HTTP server to
#[arg(short, long)] #[arg(short = 'a', long)]
pub exec: Option<String>, pub http_address: Option<SocketAddr>,
} }

View File

@ -16,7 +16,6 @@ use hyper_util::rt::TokioIo;
use log::{debug, error, info, trace}; use log::{debug, error, info, trace};
use mlua::{Error, Function, IntoLuaMulti, Table}; use mlua::{Error, Function, IntoLuaMulti, Table};
use ncr::utils::trim_header; use ncr::utils::trim_header;
use std::net::SocketAddr;
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
@ -165,13 +164,6 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
table.set("passengers", &*packet.passengers)?; table.set("passengers", &*packet.passengers)?;
call_listeners(&state, "set_passengers", table).await; call_listeners(&state, "set_passengers", table).await;
} }
ClientboundGamePacket::SetTime(packet) => {
let table = state.lua.create_table()?;
table.set("day_time", packet.day_time)?;
table.set("game_time", packet.game_time)?;
table.set("tick_day_time", packet.tick_day_time)?;
call_listeners(&state, "set_time", table).await;
}
_ => (), _ => (),
}, },
Event::Init => { Event::Init => {
@ -199,11 +191,7 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
)?; )?;
call_listeners(&state, "init", ()).await; call_listeners(&state, "init", ()).await;
let Some(address): Option<SocketAddr> = globals let Some(address) = state.http_address else {
.get::<String>("HttpAddress")
.ok()
.and_then(|string| string.parse().ok())
else {
return Ok(()); return Ok(());
}; };

View File

@ -41,7 +41,7 @@ pub async fn get_block_states(
lua: Lua, lua: Lua,
(block_names, filter_fn): (Vec<String>, Option<Function>), (block_names, filter_fn): (Vec<String>, Option<Function>),
) -> Result<Vec<u16>> { ) -> Result<Vec<u16>> {
let mut matched = Vec::with_capacity(16); let mut matched = Vec::new();
for block_name in block_names { for block_name in block_names {
for block in for block in
(u32::MIN..u32::MAX).map_while(|possible_id| BlockState::try_from(possible_id).ok()) (u32::MIN..u32::MAX).map_while(|possible_id| BlockState::try_from(possible_id).ok())

View File

@ -109,7 +109,11 @@ fn id(_lua: &Lua, client: &Client) -> Result<i32> {
} }
fn tab_list(_lua: &Lua, client: &Client) -> Result<Vec<Player>> { fn tab_list(_lua: &Lua, client: &Client) -> Result<Vec<Player>> {
Ok(client.tab_list().into_values().map(Player::from).collect()) let mut tab_list = Vec::new();
for (_, player_info) in client.tab_list() {
tab_list.push(Player::from(player_info));
}
Ok(tab_list)
} }
fn username(_lua: &Lua, client: &Client) -> Result<String> { fn username(_lua: &Lua, client: &Client) -> Result<String> {

View File

@ -56,7 +56,7 @@ pub async fn find_all_entities(
client: UserDataRef<Client>, client: UserDataRef<Client>,
(): (), (): (),
) -> Result<Vec<Table>> { ) -> Result<Vec<Table>> {
let mut matched = Vec::with_capacity(256); let mut matched = Vec::new();
for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in for (position, custom_name, kind, uuid, direction, id, owner_uuid, pose) in
get_entities!(client) get_entities!(client)
{ {

View File

@ -52,7 +52,7 @@ pub fn reload(lua: &Lua, sender: Option<String>) -> Result<(), Error> {
lua.load( lua.load(
&std::fs::read_to_string( &std::fs::read_to_string(
lua.globals() lua.globals()
.get::<String>("SCRIPT_PATH") .get::<String>("script_path")
.map_err(Error::MissingPath)?, .map_err(Error::MissingPath)?,
) )
.map_err(Error::ReadFile)?, .map_err(Error::ReadFile)?,

View File

@ -13,7 +13,7 @@ pub struct Player {
impl From<PlayerInfo> for Player { impl From<PlayerInfo> for Player {
fn from(p: PlayerInfo) -> Self { fn from(p: PlayerInfo) -> Self {
Self { Self {
display_name: p.display_name.map(|text| text.to_string()), display_name: p.display_name.map(|n| n.to_string()),
gamemode: p.gamemode.to_id(), gamemode: p.gamemode.to_id(),
latency: p.latency, latency: p.latency,
name: p.profile.name, name: p.profile.name,

View File

@ -9,7 +9,6 @@ mod lua;
mod particle; mod particle;
mod replay; mod replay;
use anyhow::Context;
use arguments::Arguments; use arguments::Arguments;
use azalea::{ use azalea::{
DefaultBotPlugins, DefaultPlugins, brigadier::prelude::CommandDispatcher, prelude::*, DefaultBotPlugins, DefaultPlugins, brigadier::prelude::CommandDispatcher, prelude::*,
@ -29,18 +28,18 @@ use std::{
collections::HashMap, collections::HashMap,
env, env,
fs::{OpenOptions, read_to_string}, fs::{OpenOptions, read_to_string},
net::SocketAddr,
path::PathBuf, path::PathBuf,
sync::Arc, sync::Arc,
}; };
#[cfg(feature = "mimalloc")] const DEFAULT_SCRIPT_PATH: &str = "errornowatcher.lua";
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
type ListenerMap = Arc<RwLock<HashMap<String, Vec<(String, Function)>>>>; type ListenerMap = Arc<RwLock<HashMap<String, Vec<(String, Function)>>>>;
#[derive(Default, Clone, Component)] #[derive(Default, Clone, Component)]
pub struct State { pub struct State {
http_address: Option<SocketAddr>,
lua: Arc<Lua>, lua: Arc<Lua>,
event_listeners: ListenerMap, event_listeners: ListenerMap,
commands: Arc<CommandDispatcher<Mutex<CommandSource>>>, commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
@ -52,21 +51,18 @@ async fn main() -> anyhow::Result<()> {
console_subscriber::init(); console_subscriber::init();
let args = Arguments::parse(); let args = Arguments::parse();
let script_path = args.script.unwrap_or(PathBuf::from("errornowatcher.lua")); let script_path = args.script.unwrap_or(PathBuf::from(DEFAULT_SCRIPT_PATH));
let event_listeners = Arc::new(RwLock::new(HashMap::new())); let event_listeners = Arc::new(RwLock::new(HashMap::new()));
let lua = unsafe { Lua::unsafe_new() }; let lua = unsafe { Lua::unsafe_new() };
let globals = lua.globals(); let globals = lua.globals();
globals.set("script_path", &*script_path)?;
lua::register_globals(&lua, &globals, event_listeners.clone())?; lua::register_globals(&lua, &globals, event_listeners.clone())?;
globals.set("SCRIPT_PATH", &*script_path)?;
lua.load( lua.load(
read_to_string(&script_path).with_context(|| format!("failed to read {script_path:?}"))?, read_to_string(script_path)
.expect(&(DEFAULT_SCRIPT_PATH.to_owned() + " should be in current directory")),
) )
.exec()?; .exec()?;
if let Some(code) = args.exec {
lua.load(code).exec()?;
}
let server = globals let server = globals
.get::<String>("Server") .get::<String>("Server")
.expect("Server should be in lua globals"); .expect("Server should be in lua globals");
@ -120,6 +116,7 @@ async fn main() -> anyhow::Result<()> {
.add_plugins(DefaultBotPlugins) .add_plugins(DefaultBotPlugins)
.set_handler(events::handle_event) .set_handler(events::handle_event)
.set_state(State { .set_state(State {
http_address: args.http_address,
lua: Arc::new(lua), lua: Arc::new(lua),
event_listeners, event_listeners,
commands: Arc::new(commands), commands: Arc::new(commands),

View File

@ -8,18 +8,17 @@ use azalea::{
protocol::packets::{PROTOCOL_VERSION, ProtocolPacket, VERSION_NAME}, protocol::packets::{PROTOCOL_VERSION, ProtocolPacket, VERSION_NAME},
}; };
use serde_json::json; use serde_json::json;
use smallvec::SmallVec;
use std::{ use std::{
fs::File, fs::File,
io::Write, io::Write,
time::{Instant, SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
use zip::{ZipWriter, write::SimpleFileOptions}; use zip::{ZipWriter, write::SimpleFileOptions};
#[derive(Resource)] #[derive(Resource)]
pub struct Recorder { pub struct Recorder {
zip_writer: ZipWriter<File>, zip_writer: ZipWriter<File>,
start: Instant, start_time: u128,
server: String, server: String,
ignore_compression: bool, ignore_compression: bool,
} }
@ -36,23 +35,21 @@ impl Recorder {
zip_writer.start_file("recording.tmcpr", SimpleFileOptions::default())?; zip_writer.start_file("recording.tmcpr", SimpleFileOptions::default())?;
Ok(Self { Ok(Self {
zip_writer, zip_writer,
start: Instant::now(), start_time: SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(),
server, server,
ignore_compression, ignore_compression,
}) })
} }
pub fn finish(mut self) -> Result<()> { pub fn finish(mut self) -> Result<()> {
let elapsed = self.start.elapsed();
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(
json!({ json!({
"singleplayer": false, "singleplayer": false,
"serverName": self.server, "serverName": self.server,
"duration": elapsed.as_millis(), "duration": SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() - self.start_time,
"date": SystemTime::now().duration_since(UNIX_EPOCH)? - elapsed, "date": self.start_time,
"mcversion": VERSION_NAME, "mcversion": VERSION_NAME,
"fileFormat": "MCPR", "fileFormat": "MCPR",
"fileFormatVersion": 14, "fileFormatVersion": 14,
@ -68,20 +65,22 @@ impl Recorder {
} }
fn get_timestamp(&self) -> Result<[u8; 4]> { fn get_timestamp(&self) -> Result<[u8; 4]> {
Ok(TryInto::<u32>::try_into(self.start.elapsed().as_millis())?.to_be_bytes()) Ok(TryInto::<u32>::try_into(
SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() - self.start_time,
)?
.to_be_bytes())
} }
fn save_raw_packet(&mut self, raw_packet: &[u8]) -> Result<()> { fn save_raw_packet(&mut self, raw_packet: &[u8]) -> Result<()> {
let mut data = Vec::with_capacity(raw_packet.len() + 8); let mut data = Vec::from(self.get_timestamp()?);
data.extend(self.get_timestamp()?); data.extend(TryInto::<u32>::try_into(raw_packet.len())?.to_be_bytes());
data.extend(&TryInto::<u32>::try_into(raw_packet.len())?.to_be_bytes());
data.extend(raw_packet); data.extend(raw_packet);
self.zip_writer.write_all(&data)?; self.zip_writer.write_all(&data)?;
Ok(()) Ok(())
} }
fn save_packet<T: ProtocolPacket>(&mut self, packet: &T) -> Result<()> { fn save_packet<T: ProtocolPacket>(&mut self, packet: &T) -> Result<()> {
let mut raw_packet = SmallVec::<[u8; 256]>::new(); let mut raw_packet = Vec::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)