From 0f348a89c30b9871355c7c81aebf908fc6a43199 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 26 Feb 2025 20:19:01 +0100 Subject: [PATCH] Add more Arguments - delay - spam_message - spam_message_delay --- Cargo.lock | 130 ++++++++++++++++++++++++++++++++++++++++++-------- Cargo.toml | 5 +- README.md | 38 +++++++++++++-- src/client.rs | 42 +++++++++++++++- src/main.rs | 24 ++++++++-- 5 files changed, 205 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0580993..ce470fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,11 +122,18 @@ dependencies = [ "crossbeam", "log", "pumpkin-protocol", + "rand 0.9.0", "simple_logger", "tokio", "uuid", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.0" @@ -177,9 +184,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -187,9 +194,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -401,9 +408,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "enum_dispatch" @@ -435,9 +442,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -643,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6da45c8333f2e152fc665d78a380be060eb84fad8ca4c9f7ac8ca29216cff0cc" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", "rand_xorshift", ] @@ -773,6 +780,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -785,7 +801,7 @@ dependencies = [ [[package]] name = "pumpkin-config" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "log", "pumpkin-util", @@ -797,7 +813,7 @@ dependencies = [ [[package]] name = "pumpkin-data" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "heck", "proc-macro2", @@ -811,7 +827,7 @@ dependencies = [ [[package]] name = "pumpkin-macros" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "heck", "proc-macro2", @@ -825,7 +841,7 @@ dependencies = [ [[package]] name = "pumpkin-nbt" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "bytes", "cesu8", @@ -836,7 +852,7 @@ dependencies = [ [[package]] name = "pumpkin-protocol" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "aes", "bytes", @@ -857,7 +873,7 @@ dependencies = [ [[package]] name = "pumpkin-util" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "bytes", "colored 3.0.0", @@ -871,7 +887,7 @@ dependencies = [ [[package]] name = "pumpkin-world" version = "0.1.0" -source = "git+https://github.com/Pumpkin-MC/Pumpkin#7c1d5eff4cfcd9fcffe762265b0ce77686675d64" +source = "git+https://github.com/Pumpkin-MC/Pumpkin#01e571946439ad9b3b40e4e1b9d47aca78f75e6a" dependencies = [ "bytes", "dashmap", @@ -915,7 +931,28 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core 0.9.2", + "zerocopy 0.8.20", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.2", ] [[package]] @@ -924,13 +961,23 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +dependencies = [ + "getrandom", + "zerocopy 0.8.20", +] + [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1015,7 +1062,7 @@ dependencies = [ [[package]] name = "serde_json5" version = "0.2.0" -source = "git+https://github.com/kralverde/serde_json5.git#6d320e959a3f596dc4313c1036eae784891466d8" +source = "git+https://github.com/kralverde/serde_json5.git#998a0b50c025fe9eac08b882593b175e8a8f43eb" dependencies = [ "pest", "pest_derive", @@ -1255,9 +1302,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" +checksum = "bd8dcafa1ca14750d8d7a05aa05988c17aab20886e1f3ae33a40223c58d92ef7" dependencies = [ "getrandom", "md-5", @@ -1473,6 +1520,47 @@ dependencies = [ "bitflags", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +dependencies = [ + "zerocopy-derive 0.8.20", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zstd" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index 119dd89..6a28d34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,11 @@ codegen-units = 1 log = "0.4" simple_logger = "5.0.0" # CLI -clap = { version = "4.5.30", features = ["derive"] } +clap = { version = "4.5.31", features = ["derive"] } # Networking tokio = { version = "1.43.0", features = ["net", "io-util"] } pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-protocol", default-features = false, features = ["packets"]} crossbeam = "0.8.4" -uuid = "1.14.0" \ No newline at end of file +uuid = "1.15.0" +rand = "0.9" diff --git a/README.md b/README.md index 2698c52..559cc0d 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,40 @@ A tool to stress test Minecraft servers by simulating numerous bot connections, helping identify performance bottlenecks and stability issues. ![Screenshot_20250223_154536](https://github.com/user-attachments/assets/b1379060-e375-44dc-95a3-91f1f2fb52dd) + ### How to Run -1. **Disable Online Mode:** Ensure that `online-mode=false` in your server's `server.properties` file. This is essential for Botmark to connect. -2. **Run Botmark:** Use the following command in your terminal: -`./botmark --ip --count ` -**Example** -`./botmark --ip 0.0.0.0:25565 --count 50` +1. **Disable Online Mode & Encryption:** Ensure that `online-mode=false` in your server's `server.properties` file. This is crucial for Botmark to connect as it simulates offline-mode clients. **(Warning: only do this in a test environment)**. +2. **Run Botmark:** Use the following command in your terminal, adjusting the arguments as needed: + + ```bash + ./botmark --ip --count [OPTIONS] + ``` + +### Command-Line Arguments + +Here's a detailed breakdown of all available command-line arguments: + +- **`--ip ` (Required):** + + - The IP address and port of the Minecraft server to stress test. + - Example: `127.0.0.1:25565` or `example.com:25565` + +- **`-c, --count ` (Optional, Default: `1`):** + + - The number of bots to simulate connecting to the server. + - Example: `--count 50` + +- **`-d, --delay ` (Optional, Default: `200`):** + - The delay (in milliseconds) between each bot connection. + - Example: `--delay 100` (100 milliseconds delay) + +- **`--spam_message ""` (Optional):** + - Will send a Chat message with the specefied `spam_message_delay`. + - Example: `--spam_message "Hello, I'm a Robot"` + +- **`--spam_message_delay ` (Optional, Default: `210`):** + - The delay (in milliseconds) between spam message. + - Example: `--spam_message_delay 100` (100 milliseconds delay) \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index b12bb3d..6f9cd6f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,7 +10,7 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::config::{SAcknowledgeFinishConfig, SKnownPacks}; use pumpkin_protocol::server::handshake::SHandShake; use pumpkin_protocol::server::login::{SLoginAcknowledged, SLoginStart}; -use pumpkin_protocol::server::play::{SConfirmTeleport, SKeepAlive}; +use pumpkin_protocol::server::play::{SChatMessage, SConfirmTeleport, SKeepAlive}; use pumpkin_protocol::{CURRENT_MC_PROTOCOL, RawPacket, ServerPacket}; use pumpkin_protocol::{ ClientPacket, CompressionLevel, CompressionThreshold, ConnectionState, @@ -18,7 +18,9 @@ use pumpkin_protocol::{ }; use std::collections::VecDeque; use std::net::SocketAddr; +use std::sync::atomic::AtomicU32; use std::sync::{Arc, atomic::AtomicBool}; +use std::time::{Instant, SystemTime, UNIX_EPOCH}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::{ net::{ @@ -42,6 +44,8 @@ pub struct Client { pub connection_reader: Mutex, pub connection_writer: Mutex, pub client_packets_queue: Arc>>, + + message_spam_cooldown: AtomicU32, } impl Client { @@ -55,6 +59,7 @@ impl Client { connection_reader: Mutex::new(connection_reader), connection_writer: Mutex::new(connection_writer), client_packets_queue: Arc::new(Mutex::new(VecDeque::new())), + message_spam_cooldown: AtomicU32::new(1), } } @@ -147,6 +152,40 @@ impl Client { } } + // TODO: make this less ugly ig + pub async fn tick(&self, spam_message: Arc>, spam_message_delay: u32) { + if self.connection_state.load() != ConnectionState::Play { + return; + } + if let Some(spam_message) = spam_message.as_ref() { + if self + .message_spam_cooldown + .fetch_sub(1, std::sync::atomic::Ordering::Relaxed) + <= 0 + { + self.message_spam_cooldown + .store(spam_message_delay, std::sync::atomic::Ordering::Relaxed); + self.send_message(spam_message.clone()).await + } + } + } + + pub async fn send_message(&self, message: String) { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + self.send_packet(&SChatMessage::new( + message, + since_the_epoch.as_millis() as i64, + rand::random(), + None, + VarInt(1), + vec![0; 20], + )) + .await; + } + /// Sends a clientbound packet to the connected client. /// /// # Arguments @@ -171,6 +210,7 @@ impl Client { } pub async fn join_server(&self, address: SocketAddr, name: String) { + dbg!(address.ip().to_string()); self.send_packet(&SHandShake { protocol_version: VarInt(CURRENT_MC_PROTOCOL.get() as i32), server_address: address.ip().to_string(), diff --git a/src/main.rs b/src/main.rs index a1ce7fb..a0f7152 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::{net::SocketAddr, sync::Arc, thread::sleep, time::Duration}; use clap::Parser; use client::Client; @@ -8,11 +8,17 @@ mod client; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] -struct Args { - #[arg(short, long)] +pub struct Args { + #[arg(long)] ip: String, #[arg(short, long, default_value_t = 1)] count: u32, + #[arg(short, long, default_value_t = 200)] + delay: u64, + #[arg(long)] + pub spam_message: Option, + #[arg(long, default_value_t = 210)] + pub spam_message_delay: u32, } #[tokio::main] @@ -20,20 +26,28 @@ async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); let args = Args::parse(); let address = args.ip.parse::().unwrap(); - log::info!("{} Bots will Join {}", args.count, address); + log::info!( + "{} Bots will Join {} with a delay of {}ms", + args.count, + address, + args.delay + ); let mut join_handles = Vec::with_capacity(args.count as usize); - for i in 0..args.count { + sleep(Duration::from_millis(args.delay)); let stream = TcpStream::connect(address) .await .expect("Failed to connect to Ip"); let client = Client::new(stream); + let message = Arc::new(args.spam_message.clone()); + client.join_server(address, format!("BOT_{}", i)).await; let join_handle = tokio::spawn(async move { loop { if !client.poll().await { break; } + client.tick(message.clone(), args.spam_message_delay).await; client.process_packets().await; } });