diff --git a/Cargo.lock b/Cargo.lock index f535f97a2..7646327d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,15 +49,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi-width" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219e3ce6f2611d83b51ec2098a12702112c29e57203a6b0a0929b2cddb486608" -dependencies = [ - "unicode-width", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -103,7 +94,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -114,7 +105,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -281,9 +272,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "shlex", @@ -310,6 +301,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.10.0-rc.6" @@ -383,6 +380,21 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "cmov" +version = "0.5.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417da527aa9bf6a1e10a781231effd1edd3ee82f27d5f8529ac9b279babce96" + [[package]] name = "colorchoice" version = "1.0.4" @@ -395,7 +407,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -466,15 +478,6 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "cookie" version = "0.18.1" @@ -635,34 +638,6 @@ dependencies = [ "parking_lot", ] -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags", - "crossterm_winapi", - "derive_more", - "document-features", - "futures-core", - "mio", - "parking_lot", - "rustix", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crunchy" version = "0.2.4" @@ -683,14 +658,14 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.10" +version = "0.7.0-rc.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6715836b4946e8585016e80b79c7561476aff3b22f7b756778e7b109d86086c6" +checksum = "37387ceb32048ff590f2cbd24d8b05fffe63c3f69a5cfa089d4f722ca4385a19" dependencies = [ + "ctutils", "num-traits", "rand_core 0.10.0-rc-3", "serdect", - "subtle", "zeroize", ] @@ -715,15 +690,24 @@ dependencies = [ [[package]] name = "crypto-primes" -version = "0.7.0-pre.4" +version = "0.7.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd9b2855017318a49714c07ee8895b89d3510d54fa6d86be5835de74c389609" +checksum = "e79c98a281f9441200b24e3151407a629bfbe720399186e50516da939195e482" dependencies = [ - "crypto-bigint 0.7.0-rc.10", + "crypto-bigint 0.7.0-rc.18", "libm", "rand_core 0.10.0-rc-3", ] +[[package]] +name = "ctutils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758e5ed90be3c8abff7f9a6f37ab7f6d8c59c2210d448b81f3f508134aec84e4" +dependencies = [ + "cmov", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -769,28 +753,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn", -] - [[package]] name = "digest" version = "0.10.7" @@ -876,6 +838,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -924,15 +892,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "ff" version = "0.13.1" @@ -945,9 +930,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "flate2" @@ -1226,6 +1211,15 @@ dependencies = [ "digest 0.11.0-rc.5", ] +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "http" version = "1.4.0" @@ -1673,11 +1667,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "log", "wasi", "windows-sys 0.61.2", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -2115,7 +2129,7 @@ dependencies = [ "rand", "rsa", "rustc-hash", - "rustyline-async", + "rustyline", "serde", "serde_json", "sha1", @@ -2309,6 +2323,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.10.0-rc.6" @@ -2418,12 +2442,12 @@ dependencies = [ [[package]] name = "rsa" -version = "0.10.0-rc.10" +version = "0.10.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e499c52862d75a86c0024cc99dcb6d7127d15af3beae7b03573d62fab7ade08a" +checksum = "c9a2b1eacbc34fbaf77f6f1db1385518446008d49b9f9f59dc9d1340fce4ca9e" dependencies = [ "const-oid 0.10.2", - "crypto-bigint 0.7.0-rc.10", + "crypto-bigint 0.7.0-rc.18", "crypto-primes", "digest 0.11.0-rc.5", "pkcs1", @@ -2432,7 +2456,6 @@ dependencies = [ "sha1", "signature 3.0.0-rc.6", "spki 0.8.0-rc.4", - "subtle", "zeroize", ] @@ -2442,15 +2465,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "1.1.3" @@ -2461,7 +2475,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2481,18 +2495,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring", "rustls-pki-types", @@ -2506,18 +2520,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "rustyline-async" -version = "0.4.7" +name = "rustyline" +version = "17.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e07ddce8399c61495b405dc94d4f30d01fc1c5e1238f10b9c09940678bc81ab" +checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564" dependencies = [ - "ansi-width", - "crossterm", - "futures-util", - "pin-project", - "thingbuf", - "thiserror", + "bitflags", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys 0.60.2", ] [[package]] @@ -2558,12 +2579,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - [[package]] name = "serde" version = "1.0.228" @@ -2685,27 +2700,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -2884,7 +2878,7 @@ dependencies = [ "fastrand", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2896,16 +2890,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thingbuf" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662b54ef6f7b4e71f683dadc787bbb2d8e8ef2f91b682ebed3164a5a7abca905" -dependencies = [ - "parking_lot", - "pin-project", -] - [[package]] name = "thiserror" version = "2.0.17" @@ -3242,9 +3226,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "untrusted" @@ -3449,7 +3433,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3473,6 +3457,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" diff --git a/Cargo.toml b/Cargo.toml index b0ac285c6..f5d68cfa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,9 +91,9 @@ pumpkin-util = { path = "pumpkin-util" } pumpkin-world = { path = "pumpkin-world" } quote = "1.0" rand = { git = "https://github.com/rust-random/rand"} -rsa = "=0.10.0-rc.10" +rsa = "=0.10.0-rc.12" rustc-hash = "2.1.1" -rustyline-async = "0.4.7" +rustyline = "17.0.2" ruzstd = "0.8.2" serde_json5 = "0.2.1" sha1 = "=0.11.0-rc.3" diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 4e9aab85a..eaad13b42 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -54,7 +54,7 @@ rand.workspace = true num-bigint.workspace = true # Console line reading -rustyline-async.workspace = true +rustyline.workspace = true # encryption rsa = { workspace = true, features = ["sha1"] } diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index fbc98a260..9401041a9 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -109,28 +109,31 @@ impl CommandDispatcher { .await { Err(InvalidConsumption(s)) => { - log::trace!( + log::debug!( "Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed" ); return Vec::new(); } Err(InvalidRequirement) => { - log::trace!( + log::debug!( "Error while parsing command \"{cmd}\": a requirement that was expected was not met." ); return Vec::new(); } Err(PermissionDenied) => { - log::trace!("Permission denied for command \"{cmd}\""); + log::debug!("Permission denied for command \"{cmd}\""); return Vec::new(); } Err(CommandFailed(_)) => { + log::debug!("Command failed"); return Vec::new(); } Ok(Some(new_suggestions)) => { suggestions.extend(new_suggestions); } - Ok(None) => {} + Ok(None) => { + log::debug!("Command none"); + } } } diff --git a/pumpkin/src/entity/ai/goal/melee_attack.rs b/pumpkin/src/entity/ai/goal/melee_attack.rs index 2c3e10eae..8ef63890f 100644 --- a/pumpkin/src/entity/ai/goal/melee_attack.rs +++ b/pumpkin/src/entity/ai/goal/melee_attack.rs @@ -121,13 +121,12 @@ impl Goal for MeleeAttackGoal { Box::pin(async { let mut target = mob.get_mob_entity().target.lock().await; - if let Some(entity) = target.as_deref() { - if !EntityPredicate::ExceptCreativeOrSpectator + if let Some(entity) = target.as_deref() + && !EntityPredicate::ExceptCreativeOrSpectator .test(entity.get_entity()) .await - { - *target = None; - } + { + *target = None; } let mut navigator = mob.get_mob_entity().navigator.lock().await; diff --git a/pumpkin/src/entity/mob/creeper.rs b/pumpkin/src/entity/mob/creeper.rs index 5f2e0f4e0..d68bab0ff 100644 --- a/pumpkin/src/entity/mob/creeper.rs +++ b/pumpkin/src/entity/mob/creeper.rs @@ -54,7 +54,6 @@ impl CreeperEntity { } pub async fn set_fuse_speed(&self, speed: i32) { - dbg!(speed); self.fuse_speed.store(speed, Ordering::Relaxed); self.mob_entity .living_entity @@ -64,7 +63,7 @@ impl CreeperEntity { MetaDataType::Integer, speed, )]) - .await + .await; } } diff --git a/pumpkin/src/entity/mob/drowned.rs b/pumpkin/src/entity/mob/drowned.rs index c039de477..ab64c298c 100644 --- a/pumpkin/src/entity/mob/drowned.rs +++ b/pumpkin/src/entity/mob/drowned.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc}; +use std::sync::Arc; use pumpkin_data::entity::EntityType; diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index 8dc5bd274..621d1d8f1 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -70,7 +70,7 @@ impl MobEntity { } pub async fn set_attacking(&self, attacking: bool) { - self.set_mob_flag(Self::ATTACKING_FLAG, attacking).await + self.set_mob_flag(Self::ATTACKING_FLAG, attacking).await; } async fn set_mob_flag(&self, flag: u8, value: bool) { diff --git a/pumpkin/src/lib.rs b/pumpkin/src/lib.rs index 3c4cb9f3c..d81e8037c 100644 --- a/pumpkin/src/lib.rs +++ b/pumpkin/src/lib.rs @@ -1,7 +1,7 @@ // Not warn event sending macros #![allow(unused_labels)] -use crate::logging::{GzipRollingLogger, ReadlineLogWrapper}; +use crate::logging::{GzipRollingLogger, PumpkinCommandCompleter, ReadlineLogWrapper}; use crate::net::DisconnectReason; use crate::net::bedrock::BedrockClient; use crate::net::java::JavaClient; @@ -16,7 +16,9 @@ use pumpkin_macros::send_cancellable; use pumpkin_protocol::ConnectionState::Play; use pumpkin_util::permission::{PermissionManager, PermissionRegistry}; use pumpkin_util::text::TextComponent; -use rustyline_async::{Readline, ReadlineEvent}; +use rustyline::Editor; +use rustyline::history::FileHistory; +use rustyline::{Config, error::ReadlineError}; use std::collections::HashMap; use std::io::{Cursor, IsTerminal, stdin}; use std::str::FromStr; @@ -79,7 +81,6 @@ pub fn init_logger(advanced_config: &AdvancedConfiguration) { config.set_level_color(level, None); } } else { - // We are technically logging to a file-like object. config.set_write_log_enable_colors(true); } @@ -117,11 +118,25 @@ pub fn init_logger(advanced_config: &AdvancedConfiguration) { ) }; - let (logger, rl): (Box, _) = if advanced_config.commands.use_tty - && stdin().is_terminal() - { - match Readline::new("$ ".to_owned()) { - Ok((rl, stdout)) => (WriteLogger::new(level, config.build(), stdout), Some(rl)), + let (logger, rl): ( + Box, + Option>, + ) = if advanced_config.commands.use_tty && stdin().is_terminal() { + let rl_config = Config::builder() + .auto_add_history(true) + .completion_type(rustyline::CompletionType::List) + .edit_mode(rustyline::EditMode::Emacs) + .build(); + let helper = PumpkinCommandCompleter::new(); + + match Editor::with_config(rl_config) { + Ok(mut rl) => { + rl.set_helper(Some(helper)); + ( + WriteLogger::new(level, config.build(), std::io::stdout()), + Some(rl), + ) + } Err(e) => { log::warn!( "Failed to initialize console input ({e}); falling back to simple logger" @@ -330,7 +345,6 @@ impl PumpkinServer { log::info!("Completed save!"); - // Explicitly drop the line reader to return the terminal to the original state. if let Some((wrapper, _)) = LOGGER_IMPL.wait() && let Some(rl) = wrapper.take_readline() { @@ -499,43 +513,38 @@ async fn setup_stdin_console(server: Arc) { }); } -fn setup_console(rl: Readline, server: Arc) { - // This needs to be async, or it will hog a thread. - server.clone().spawn_task(async move { - let mut rl = rl; +fn setup_console(mut rl: Editor, server: Arc) { + let (tx, mut rx) = tokio::sync::mpsc::channel(1); + + if let Some(helper) = rl.helper_mut() { + if let Ok(mut server_lock) = helper.server.write() { + *server_lock = Some(server.clone()); + } + let _ = helper.rt.set(tokio::runtime::Handle::current()); + } + + std::thread::spawn(move || { while !SHOULD_STOP.load(Ordering::Relaxed) { - let t1 = rl.readline(); - let t2 = STOP_INTERRUPT.notified(); - - let result = select! { - line = t1 => Some(line), - () = t2 => None, - }; - - let Some(result) = result else { break }; - - match result { - Ok(ReadlineEvent::Line(line)) => { - send_cancellable! {{ - ServerCommandEvent::new(line.clone()); - - 'after: { - let dispatcher = server.command_dispatcher.read().await; - - dispatcher - .handle_command(&command::CommandSender::Console, &server, &line) - .await; - rl.add_history_entry(line).unwrap(); - } - }} + let readline = rl.readline("$ "); + match readline { + Ok(line) => { + let _ = rl.add_history_entry(line.clone()); + if tx.blocking_send(line).is_err() { + break; + } } - Ok(ReadlineEvent::Interrupted) => { + Err(ReadlineError::Interrupted) => { + log::info!("CTRL-C"); stop_server(); break; } - err => { - log::error!("Console command loop failed!"); - log::error!("{err:?}"); + Err(ReadlineError::Eof) => { + log::info!("CTRL-D"); + stop_server(); + break; + } + Err(err) => { + log::error!("Error reading console input: {err}"); break; } } @@ -543,7 +552,34 @@ fn setup_console(rl: Readline, server: Arc) { if let Some((wrapper, _)) = LOGGER_IMPL.wait() { wrapper.return_readline(rl); } + }); + server.clone().spawn_task(async move { + while !SHOULD_STOP.load(Ordering::Relaxed) { + let t1 = rx.recv(); + let t2 = STOP_INTERRUPT.notified(); + + let result = select! { + line = t1 => line, + () = t2 => None, + }; + + if let Some(line) = result { + send_cancellable! {{ + ServerCommandEvent::new(line.clone()); + + 'after: { + let dispatcher = server.command_dispatcher.read().await; + + dispatcher + .handle_command(&command::CommandSender::Console, &server, &line) + .await; + } + }} + } else { + break; + } + } log::debug!("Stopped console commands task"); }); } diff --git a/pumpkin/src/logging.rs b/pumpkin/src/logging.rs index 206af6088..60598f997 100644 --- a/pumpkin/src/logging.rs +++ b/pumpkin/src/logging.rs @@ -3,14 +3,26 @@ use flate2::write::GzEncoder; use log::{LevelFilter, Log, Record}; -use rustyline_async::Readline; +use rustyline::completion::Completer; +use rustyline::highlight::Highlighter; +use rustyline::hint::Hinter; +use rustyline::history::FileHistory; +use rustyline::validate::Validator; +use rustyline::{Editor, Helper}; use simplelog::{CombinedLogger, Config, SharedLogger, WriteLogger}; +use std::borrow::Cow; +use std::collections::VecDeque; use std::fmt::format; use std::fs::File; use std::io::{self, BufWriter}; use std::path::PathBuf; +use std::sync::Arc; use time::{Duration, OffsetDateTime, UtcOffset}; +use crate::command::CommandSender; +use crate::command::tree::NodeType; +use crate::server::Server; + const LOG_DIR: &str = "logs"; const MAX_ATTEMPTS: u32 = 100; @@ -18,7 +30,7 @@ const MAX_ATTEMPTS: u32 = 100; /// properly flush logs to the output while they happen instead of batched pub struct ReadlineLogWrapper { internal: Box, - readline: std::sync::Mutex>, + readline: std::sync::Mutex>>, } struct GzipRollingLoggerData { @@ -34,6 +46,20 @@ pub struct GzipRollingLogger { config: Config, } +impl SharedLogger for GzipRollingLogger { + fn level(&self) -> LevelFilter { + self.log_level + } + + fn config(&self) -> Option<&Config> { + Some(&self.config) + } + + fn as_log(self: Box) -> Box { + Box::new(*self) + } +} + impl GzipRollingLogger { pub fn new( log_level: LevelFilter, @@ -190,26 +216,12 @@ impl Log for GzipRollingLogger { } } -impl SharedLogger for GzipRollingLogger { - fn level(&self) -> LevelFilter { - self.log_level - } - - fn config(&self) -> Option<&Config> { - Some(&self.config) - } - - fn as_log(self: Box) -> Box { - Box::new(*self) - } -} - impl ReadlineLogWrapper { #[must_use] pub fn new( log: Box, file_logger: Option>, - rl: Option, + rl: Option>, ) -> Self { let loggers: Vec>> = vec![Some(log), file_logger]; Self { @@ -218,18 +230,15 @@ impl ReadlineLogWrapper { } } - pub fn take_readline(&self) -> Option { + pub fn take_readline(&self) -> Option> { self.readline .lock() .map_or_else(|_| None, |mut result| result.take()) } - // This isn't really dead code, just for some reason rust thinks that it might be. - // Schroedinger's dead code -> expect warns unfulfilled lint expectation but removing it causes dead_code lint? #[allow(dead_code)] - pub(crate) fn return_readline(&self, rl: Readline) { + pub(crate) fn return_readline(&self, rl: Editor) { if let Ok(mut result) = self.readline.lock() { - println!("Returned rl"); let _ = result.insert(rl); } } @@ -239,23 +248,208 @@ impl ReadlineLogWrapper { impl Log for ReadlineLogWrapper { fn log(&self, record: &log::Record) { self.internal.log(record); - if let Ok(mut lock) = self.readline.lock() - && let Some(rl) = lock.as_mut() - { - let _ = rl.flush(); - } } fn flush(&self) { self.internal.flush(); - if let Ok(mut lock) = self.readline.lock() - && let Some(rl) = lock.as_mut() - { - let _ = rl.flush(); - } } fn enabled(&self, metadata: &log::Metadata) -> bool { self.internal.enabled(metadata) } } + +#[derive(Clone, Default)] +pub struct PumpkinCommandCompleter { + pub server: Arc>>>, + pub rt: Arc>, +} + +impl PumpkinCommandCompleter { + #[must_use] + pub fn new() -> Self { + Self { + server: Arc::new(std::sync::RwLock::new(None)), + rt: Arc::new(std::sync::OnceLock::new()), + } + } +} + +impl Helper for PumpkinCommandCompleter {} +impl Highlighter for PumpkinCommandCompleter { + fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { + line.find(' ').map_or_else( + || Cow::Owned(format!("\x1b[1;36m{line}\x1b[0m")), + |first_space| { + let (cmd, args) = line.split_at(first_space); + Cow::Owned(format!("\x1b[1;36m{cmd}\x1b[0m{args}")) + }, + ) + } +} +impl Hinter for PumpkinCommandCompleter { + type Hint = String; + fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { + if line.is_empty() || pos < line.len() { + return None; + } + + if let Ok((_, candidates)) = self.complete(line, pos, ctx) + && let Some(first) = candidates.first() + { + let last_word = line.split_whitespace().last().unwrap_or(""); + if first.starts_with('<') { + return line.ends_with(' ').then(|| first.clone()); + } + + if let Some(stripped) = first.strip_prefix(last_word) { + return Some(stripped.to_string()); + } + } + None + } +} + +impl Validator for PumpkinCommandCompleter {} + +impl Completer for PumpkinCommandCompleter { + type Candidate = String; + + #[expect(clippy::too_many_lines)] + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &rustyline::Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + let cmd_to_cursor = &line[..pos]; + let has_slash = cmd_to_cursor.starts_with('/'); + let cmd = if has_slash { + &cmd_to_cursor[1..] + } else { + cmd_to_cursor + }; + + let Some(handle) = self.rt.get() else { + return Ok((0, Vec::new())); + }; + let Ok(server_guard) = self.server.try_read() else { + return Ok((0, Vec::new())); + }; + let Some(server) = server_guard.as_ref() else { + return Ok((0, Vec::new())); + }; + + let parts: Vec<&str> = cmd.split_whitespace().collect(); + let ends_with_space = cmd.ends_with(' '); + + handle.block_on(async { + let dispatcher = server.command_dispatcher.read().await; + let src = CommandSender::Console; + + if parts.is_empty() || (parts.len() == 1 && !ends_with_space) { + let typing = parts.first().unwrap_or(&""); + let candidates = dispatcher + .commands + .keys() + .filter(|k| k.starts_with(typing)) + .cloned() + .collect(); + return Ok((usize::from(has_slash), candidates)); + } + + let Some(tree) = dispatcher.get_tree(parts[0]).ok() else { + return Ok((0, Vec::new())); + }; + + let mut current_indices = tree.children.clone(); + let mut word_index = 1; + let walk_limit = if ends_with_space { + parts.len() + } else { + parts.len() - 1 + }; + + while word_index < walk_limit { + let token = parts[word_index]; + let mut next_indices = Vec::new(); + + let mut worklist: VecDeque = current_indices.iter().copied().collect(); + + while let Some(idx) = worklist.pop_front() { + let node = &tree.nodes[idx]; + + match &node.node_type { + NodeType::Require { predicate } => { + if predicate(&src) { + worklist.extend(node.children.iter().copied()); + } + } + NodeType::Literal { string } => { + if string.eq_ignore_ascii_case(token) { + next_indices.extend(node.children.iter().copied()); + } + } + NodeType::Argument { .. } => { + next_indices.extend(node.children.iter().copied()); + } + NodeType::ExecuteLeaf { .. } => {} + } + } + + if next_indices.is_empty() { + return Ok((0, Vec::new())); + } + + current_indices = next_indices; + word_index += 1; + } + + let typing = if ends_with_space { + "" + } else { + parts.last().unwrap_or(&"") + }; + let mut candidates = Vec::new(); + + let mut suggestion_worklist: VecDeque = current_indices.into_iter().collect(); + + while let Some(idx) = suggestion_worklist.pop_front() { + let node = &tree.nodes[idx]; + match &node.node_type { + NodeType::Require { predicate } => { + if predicate(&src) { + suggestion_worklist.extend(node.children.iter().copied()); + } + } + NodeType::Literal { string } => { + if string.starts_with(typing) { + candidates.push(string.clone()); + } + } + NodeType::Argument { name, consumer } => { + let suggest_future = consumer.suggest(&src, server, typing); + + if let Ok(Some(suggestions)) = suggest_future.await { + for s in suggestions { + let s = s.suggestion; + if s.starts_with(typing) { + candidates.push(s); + } + } + } else { + let placeholder = format!("<{name}>"); + if placeholder.starts_with(typing) || typing.is_empty() { + candidates.push(placeholder); + } + } + } + NodeType::ExecuteLeaf { executor: _ } => {} + } + } + + let last_space = cmd.rfind(' ').map_or(0, |i| i + 1); + Ok((last_space + usize::from(has_slash), candidates)) + }) + } +}