diff --git a/Cargo.lock b/Cargo.lock index d4fa4f970..ff0a3cf88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,9 +125,9 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-compression" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" +checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" dependencies = [ "compression-codecs", "compression-core", @@ -416,9 +416,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" +checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" dependencies = [ "compression-core", "flate2", @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" [[package]] name = "console-api" @@ -655,13 +655,13 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossfire" -version = "2.1.6" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6b5ad0a8e719cef020e136986d534068ffd35df287ab1635ff242585c02213e" +checksum = "121161e240fcedaef95067636249fd1200e76921a2f0b101dca1648ba6d7ffa5" dependencies = [ "crossbeam-queue", "crossbeam-utils", - "futures", + "futures-core", "parking_lot", ] diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 47987fcf4..2ae1dfe5a 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -27,7 +27,7 @@ aes = "0.8" cfb8 = "0.8" # compression -async-compression = { version = "0.4.33", features = ["tokio", "zlib"] } +async-compression = { version = "0.4.34", features = ["tokio", "zlib"] } take_mut = "0.2.2" bitflags = "2.10.0" diff --git a/pumpkin-protocol/src/codec/var_int.rs b/pumpkin-protocol/src/codec/var_int.rs index 46d7222da..daa83c4f8 100644 --- a/pumpkin-protocol/src/codec/var_int.rs +++ b/pumpkin-protocol/src/codec/var_int.rs @@ -38,15 +38,15 @@ impl VarInt { } pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> { - let mut val = self.0; - loop { - let b: u8 = val as u8 & 0x7F; + // Must cast to u32 to prevent infinite loops on negative i32s + let mut val = self.0 as u32; + + while val > 0x7F { + write.write_u8((val as u8) | 0x80)?; val >>= 7; - write.write_u8(if val == 0 { b } else { b | 0x80 })?; - if val == 0 { - break; - } } + + write.write_u8(val as u8)?; Ok(()) } @@ -201,17 +201,14 @@ impl<'de> Deserialize<'de> for VarInt { impl PacketWrite for VarInt { fn write(&self, writer: &mut W) -> Result<(), Error> { - let mut val = (self.0 << 1) ^ (self.0 >> 31); - loop { - let b: u8 = val as u8 & 0b01111111; + let mut val = ((self.0 << 1) ^ (self.0 >> 31)) as u32; + + while val > 0x7F { + ((val as u8 & 0x7F) | 0x80).write(writer)?; val >>= 7; - if val == 0 { - b.write(writer)?; - break; - } else { - (b | 0b10000000).write(writer)?; - }; } + + (val as u8).write(writer)?; Ok(()) } } diff --git a/pumpkin-protocol/src/codec/var_long.rs b/pumpkin-protocol/src/codec/var_long.rs index 297fa9a93..b85ef94b1 100644 --- a/pumpkin-protocol/src/codec/var_long.rs +++ b/pumpkin-protocol/src/codec/var_long.rs @@ -27,27 +27,15 @@ impl VarLong { /// The maximum number of bytes a `VarLong` can occupy. const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap(); - /// Returns the exact number of bytes this VarLong will write when - /// [`Encode::encode`] is called, assuming no error occurs. - pub fn written_size(&self) -> usize { - match self.0 { - 0 => 1, - n => (31 - n.leading_zeros() as usize) / 7 + 1, - } - } - pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> { - let mut x = self.0; - loop { - let byte = (x & 0x7F) as u8; - x >>= 7; - if x == 0 { - write.write_u8(byte)?; - break; - } - write.write_u8(byte | 0x80)?; + let mut val = self.0 as u64; + + while val > 0x7F { + write.write_u8((val as u8) | 0x80)?; + val >>= 7; } + write.write_u8(val as u8)?; Ok(()) } @@ -56,8 +44,8 @@ impl VarLong { let mut val = 0; for i in 0..Self::MAX_SIZE.get() { let byte = read.get_u8()?; - val |= (i64::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { + val |= (i64::from(byte) & 0x7F) << (i * 7); + if byte & 0x80 == 0 { return Ok(VarLong(val)); } } @@ -158,15 +146,14 @@ impl<'de> Deserialize<'de> for VarLong { impl PacketWrite for VarLong { fn write(&self, writer: &mut W) -> Result<(), Error> { - let mut value = (self.0 << 1) ^ (self.0 >> 63); - loop { - let b: u8 = value as u8 & 127; - value >>= 7; - writer.write_all(&if value == 0 { [b] } else { [b | 128] })?; - if value == 0 { - break; - } + let mut val = ((self.0 << 1) ^ (self.0 >> 63)) as u64; + + while val > 0x7F { + ((val as u8 & 0x7F) | 0x80).write(writer)?; + val >>= 7; } + + (val as u8).write(writer)?; Ok(()) } } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 7706c5ac1..017416d5a 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -49,7 +49,7 @@ rand = "=0.10.0-rc.5" num_cpus = "1.17.0" rustc-hash = "2.1.1" slotmap = "1.0" -crossfire = "2.1.6" +crossfire = "2.1.7" [dev-dependencies] criterion = { version = "0.7", default-features = false, features = ["html_reports", "async_tokio"] } diff --git a/pumpkin/src/logging.rs b/pumpkin/src/logging.rs index a9a2d63db..59f16f829 100644 --- a/pumpkin/src/logging.rs +++ b/pumpkin/src/logging.rs @@ -4,8 +4,12 @@ use rustyline_async::Readline; use simplelog::{CombinedLogger, Config, SharedLogger, WriteLogger}; use std::fmt::format; use std::fs::File; -use std::io::BufWriter; -use std::path::Path; +use std::io::{self, BufWriter}; +use std::path::PathBuf; +use time::{Duration, OffsetDateTime, UtcOffset}; + +const LOG_DIR: &str = "logs"; +const MAX_ATTEMPTS: u32 = 100; /// A wrapper for our logger to hold the terminal input while no input is expected in order to /// properly flush logs to the output while they happen instead of batched @@ -34,65 +38,85 @@ impl GzipRollingLogger { filename: String, ) -> Result, Box> { let now = time::OffsetDateTime::now_utc(); - std::fs::create_dir_all("logs")?; + std::fs::create_dir_all(LOG_DIR)?; + + let latest_path = PathBuf::from(LOG_DIR).join(&filename); // If latest.log exists, we will gzip it - if Path::new(&format!("logs/{filename}")).exists() { - let new_filename = Self::new_filename(false); - let mut file = File::open(format!("logs/{filename}"))?; + if latest_path.exists() { + eprintln!( + "Found existing log file at '{}', gzipping it now...", + latest_path.display() + ); + + let new_gz_path = Self::new_filename(true)?; + + let mut file = File::open(&latest_path)?; + let mut encoder = GzEncoder::new( - BufWriter::new(File::create(&new_filename)?), + BufWriter::new(File::create(&new_gz_path)?), flate2::Compression::best(), ); - std::io::copy(&mut file, &mut encoder)?; + + io::copy(&mut file, &mut encoder)?; encoder.finish()?; + + std::fs::remove_file(&latest_path)?; } + let new_logger = WriteLogger::new(log_level, config.clone(), File::create(&latest_path)?); + Ok(Box::new(Self { log_level, data: std::sync::Mutex::new(GzipRollingLoggerData { current_day_of_month: now.day(), last_rotate_time: now, - latest_filename: filename.clone(), - latest_logger: *WriteLogger::new( - log_level, - config.clone(), - File::create(format!("logs/{filename}")).unwrap(), - ), + latest_filename: filename, + latest_logger: *new_logger, }), config, })) } - pub fn new_filename(yesterday: bool) -> String { - let mut now = time::OffsetDateTime::now_utc() - .to_offset(time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC)); - if yesterday { - now -= time::Duration::days(1) - } - let base_filename = format!("{}-{:02}-{:02}", now.year(), now.month() as u8, now.day()); + pub fn new_filename(yesterday: bool) -> Result> { + let local_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC); + let mut now = OffsetDateTime::now_utc().to_offset(local_offset); - let mut id = 1; - loop { - let filename = format!("logs/{base_filename}-{id}.log.gz"); - if !Path::new(&filename).exists() { - return filename; - } - id += 1; + if yesterday { + now -= Duration::days(1); } + + let date_format = format!("{}-{:02}-{:02}", now.year(), now.month() as u8, now.day()); + + let log_path = PathBuf::from(LOG_DIR); + + for id in 1..=MAX_ATTEMPTS { + let filename = log_path.join(format!("{}-{}.log.gz", date_format, id)); + + if !filename.exists() { + return Ok(filename); + } + } + + Err(format!( + "Failed to find a unique log filename for date {} after {} attempts.", + date_format, MAX_ATTEMPTS + ) + .into()) } fn rotate_log(&self) -> Result<(), Box> { let now = time::OffsetDateTime::now_utc(); let mut data = self.data.lock().unwrap(); - let new_filename = Self::new_filename(true); - let mut file = File::open(format!("logs/{}", data.latest_filename))?; + let new_gz_path = Self::new_filename(true)?; + let latest_path = PathBuf::from(LOG_DIR).join(&data.latest_filename); + let mut file = File::open(&latest_path)?; let mut encoder = GzEncoder::new( - BufWriter::new(File::create(format!("logs/{new_filename}"))?), + BufWriter::new(File::create(&new_gz_path)?), flate2::Compression::best(), ); - std::io::copy(&mut file, &mut encoder)?; + io::copy(&mut file, &mut encoder)?; encoder.finish()?; data.current_day_of_month = now.day(); @@ -100,7 +124,7 @@ impl GzipRollingLogger { data.latest_logger = *WriteLogger::new( self.log_level, self.config.clone(), - File::create(format!("logs/{}", data.latest_filename)).unwrap(), + File::create(&latest_path)?, ); Ok(()) } @@ -108,21 +132,19 @@ impl GzipRollingLogger { fn remove_ansi_color_code(s: &str) -> String { let mut result = String::with_capacity(s.len()); - let mut in_escape_sequence = false; + let mut it = s.chars(); - for c in s.chars() { - if in_escape_sequence { - if c.is_ascii_alphabetic() { - // This broadly covers 'm', 'J', 'H', etc. - in_escape_sequence = false; + while let Some(c) = it.next() { + if c == '\x1b' { + for c_seq in it.by_ref() { + if c_seq.is_ascii_alphabetic() { + break; + } } - } else if c == '\x1B' { - in_escape_sequence = true; } else { result.push(c); } } - result } diff --git a/pumpkin/src/net/java/mod.rs b/pumpkin/src/net/java/mod.rs index 039a5c453..e98235e05 100644 --- a/pumpkin/src/net/java/mod.rs +++ b/pumpkin/src/net/java/mod.rs @@ -159,10 +159,7 @@ impl JavaClient { /// /// * `server`: A reference to the `Server` instance. pub async fn process_packets(self: &Arc, server: &Arc) { - loop { - let packet = self.get_packet().await; - let Some(packet) = packet else { break }; - + while let Some(packet) = self.get_packet().await { if let Err(error) = self.handle_packet(server, &packet).await { let text = format!("Error while reading incoming packet {error}"); log::error!(