Add more Arguments

- delay
- spam_message
- spam_message_delay
This commit is contained in:
Alexander Medvedev 2025-02-26 20:19:01 +01:00
parent 634f9cb00c
commit 0f348a89c3
5 changed files with 205 additions and 34 deletions

130
Cargo.lock generated
View File

@ -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"

View File

@ -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"
uuid = "1.15.0"
rand = "0.9"

View File

@ -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)
</div>
### 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 <ip:port> --count <bot-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 <ip:port> --count <bot-count> [OPTIONS]
```
### Command-Line Arguments
Here's a detailed breakdown of all available command-line arguments:
- **`--ip <ip:port>` (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 <bot-count>` (Optional, Default: `1`):**
- The number of bots to simulate connecting to the server.
- Example: `--count 50`
- **`-d, --delay <delay>` (Optional, Default: `200`):**
- The delay (in milliseconds) between each bot connection.
- Example: `--delay 100` (100 milliseconds delay)
- **`--spam_message "<message>"` (Optional):**
- Will send a Chat message with the specefied `spam_message_delay`.
- Example: `--spam_message "Hello, I'm a Robot"`
- **`--spam_message_delay <delay>` (Optional, Default: `210`):**
- The delay (in milliseconds) between spam message.
- Example: `--spam_message_delay 100` (100 milliseconds delay)

View File

@ -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<OwnedReadHalf>,
pub connection_writer: Mutex<OwnedWriteHalf>,
pub client_packets_queue: Arc<Mutex<VecDeque<RawPacket>>>,
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<Option<String>>, 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(),

View File

@ -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<String>,
#[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::<SocketAddr>().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;
}
});