Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d3619f838 | ||
|
|
85ff0f4e28 | ||
|
|
0e4faf82d6 | ||
|
|
253ad6f336 | ||
|
|
4f493976ea | ||
|
|
a7296b4522 | ||
|
|
e4fa37f2c9 |
6
.github/workflows/rust.yml
vendored
6
.github/workflows/rust.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
toolchain:
|
toolchain:
|
||||||
- stable
|
- stable
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo fmt --check
|
- run: cargo fmt --check
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
toolchain:
|
toolchain:
|
||||||
- stable
|
- stable
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo clippy --all-targets --all-features
|
- run: cargo clippy --all-targets --all-features
|
||||||
@ -42,7 +42,7 @@ jobs:
|
|||||||
toolchain:
|
toolchain:
|
||||||
- stable
|
- stable
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo test --verbose
|
- run: cargo test --verbose
|
||||||
|
|||||||
1300
Cargo.lock
generated
1300
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "botmark"
|
name = "botmark"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@ -10,15 +10,21 @@ codegen-units = 1
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# log
|
# log
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simple_logger = "5.1.0"
|
simple_logger = "5.2.0"
|
||||||
# CLI
|
# CLI
|
||||||
clap = { version = "4.5.54", features = ["derive"] }
|
clap = { version = "4.6.6", features = ["derive"] }
|
||||||
# Networking
|
# Networking
|
||||||
tokio = { version = "1.49.0", features = ["net", "io-util"] }
|
tokio = { version = "1.53.1", features = ["net", "io-util"] }
|
||||||
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-protocol", default-features = false, features = ["serverbound", "clientbound"]}
|
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-protocol", default-features = false, features = ["serverbound", "clientbound"]}
|
||||||
pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-util"}
|
pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-util"}
|
||||||
pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-data"}
|
pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-data"}
|
||||||
|
|
||||||
crossbeam = "0.8.4"
|
crossbeam = "0.8.4"
|
||||||
uuid = "1.19.0"
|
uuid = "1.25.0"
|
||||||
rand = { git = "https://github.com/rust-random/rand"}
|
rand = { git = "https://github.com/rust-random/rand"}
|
||||||
|
rsa = { version = "=0.10.0-rc.18", default-features = false, features = ["std", "encoding"] }
|
||||||
|
|
||||||
|
[patch."https://github.com/Pumpkin-MC/Pumpkin"]
|
||||||
|
pumpkin-protocol = { path = "../Pumpkin/crates/pumpkin-protocol" }
|
||||||
|
pumpkin-util = { path = "../Pumpkin/crates/pumpkin-util" }
|
||||||
|
pumpkin-data = { path = "../Pumpkin/crates/pumpkin-data" }
|
||||||
|
|||||||
32
README.md
32
README.md
@ -1,7 +1,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||

|

|
||||||
|
|
||||||
A tool to stress test Minecraft servers by simulating numerous bot connections, helping identify performance bottlenecks and stability issues.
|
A tool to stress test Minecraft servers by simulating numerous bot connections, helping identify performance bottlenecks and stability issues.
|
||||||

|

|
||||||
@ -39,10 +39,32 @@ Here's a detailed breakdown of all available command-line arguments:
|
|||||||
- The timeout (in milliseconds) for each bot connection.
|
- The timeout (in milliseconds) for each bot connection.
|
||||||
- Example: `--delay 500` (500 milliseconds delay)
|
- Example: `--delay 500` (500 milliseconds delay)
|
||||||
|
|
||||||
- **`--spam_message "<message>"` (Optional):**
|
- **`--enable_spam_message <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable periodic chat spam messages.
|
||||||
|
- Example: `--enable_spam_message true`
|
||||||
|
|
||||||
|
- **`--spam_message "<message>"` (Optional, Default: `Please do not spam!`):**
|
||||||
- Will send a Chat message with the specified `spam_message_delay`.
|
- Will send a Chat message with the specified `spam_message_delay`.
|
||||||
- Example: `--spam_message "Hello, I'm a Robot"`
|
- Example: `--spam_message "Hello, I'm a Robot"`
|
||||||
|
|
||||||
- **`--spam_message_delay <delay>` (Optional, Default: `210`):**
|
- **`--spam_message_delay_min <delay>` (Optional, Default: `150`):**
|
||||||
- The delay (in milliseconds) between spam message.
|
- The minimal delay (in milliseconds) between spam message.
|
||||||
- Example: `--spam_message_delay 100` (100 milliseconds delay)
|
- Example: `--spam_message_delay_min 100` (100 milliseconds delay)
|
||||||
|
- **`--spam_message_delay_max <delay>` (Optional, Default: `250`):**
|
||||||
|
- The maximal delay (in milliseconds) between spam message.
|
||||||
|
- Example: `--spam_message_delay_max 250` (250 milliseconds delay)
|
||||||
|
- **`--enable_rotation <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable random head rotations.
|
||||||
|
- Example: `--enable_rotation true`
|
||||||
|
- **`--enable_swing <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable random arm swings.
|
||||||
|
- Example: `--enable_swing true`
|
||||||
|
- **`--enable_movement <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable random bot movement and walking.
|
||||||
|
- Example: `--enable_movement true`
|
||||||
|
- **`--enable_jumping <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable jumping while moving.
|
||||||
|
- Example: `--enable_jumping true`
|
||||||
|
- **`--enable_physics <false/true>` (Optional, Default: `true`):**
|
||||||
|
- Enable physics simulation (gravity, friction, inertia, server knockback).
|
||||||
|
- Example: `--enable_physics true`
|
||||||
1614
src/client.rs
1614
src/client.rs
File diff suppressed because it is too large
Load Diff
72
src/main.rs
72
src/main.rs
@ -1,4 +1,4 @@
|
|||||||
use std::{net::SocketAddr, sync::Arc, thread::sleep, time::Duration};
|
use std::{net::SocketAddr, sync::Arc, sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use client::Client;
|
use client::Client;
|
||||||
@ -17,31 +17,47 @@ pub struct Args {
|
|||||||
pub delay: u64,
|
pub delay: u64,
|
||||||
#[arg(short, long, default_value_t = 5000)]
|
#[arg(short, long, default_value_t = 5000)]
|
||||||
pub timeout: u64,
|
pub timeout: u64,
|
||||||
#[arg(long)]
|
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_spam_message")]
|
||||||
|
pub enable_spam_message: bool,
|
||||||
|
#[arg(long, default_value = "Please do not spam!", alias = "spam_message")]
|
||||||
pub spam_message: Option<String>,
|
pub spam_message: Option<String>,
|
||||||
#[arg(long, default_value_t = 210)]
|
#[arg(long, default_value_t = 150, alias = "spam_message_delay_min")]
|
||||||
pub spam_message_delay: u32,
|
pub spam_message_delay_min: u32,
|
||||||
|
#[arg(long, default_value_t = 250, alias = "spam_message_delay_max")]
|
||||||
|
pub spam_message_delay_max: u32,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_rotation")]
|
||||||
|
pub enable_rotation: bool,
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_swing")]
|
||||||
|
pub enable_swing: bool,
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_movement")]
|
||||||
|
pub enable_movement: bool,
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_jumping")]
|
||||||
|
pub enable_jumping: bool,
|
||||||
|
#[arg(long, default_value_t = true, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set, alias = "enable_physics")]
|
||||||
|
pub enable_physics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
simple_logger::init_with_level(log::Level::Info).unwrap();
|
simple_logger::init_with_level(log::Level::Info).unwrap();
|
||||||
let args = Args::parse();
|
let args = Arc::new(Args::parse());
|
||||||
let address = args.ip.parse::<SocketAddr>().unwrap();
|
let address = args.ip.parse::<SocketAddr>().unwrap();
|
||||||
|
let bot_count = args.count;
|
||||||
log::info!(
|
log::info!(
|
||||||
"{} Bots will Join {} with a delay of {}ms",
|
"{} Bots will Join {} with a delay of {}ms",
|
||||||
args.count,
|
bot_count,
|
||||||
address,
|
address,
|
||||||
args.delay
|
args.delay
|
||||||
);
|
);
|
||||||
let mut join_handles = Vec::with_capacity(args.count as usize);
|
let timeout_dur = Duration::from_millis(args.timeout);
|
||||||
for i in 0..args.count {
|
let timeout_delay = Duration::from_millis(args.delay);
|
||||||
sleep(Duration::from_millis(args.delay));
|
|
||||||
let stream_result = timeout(
|
let mut join_handles = Vec::with_capacity(bot_count as usize);
|
||||||
Duration::from_millis(args.timeout),
|
for i in 0..bot_count {
|
||||||
TcpStream::connect(address),
|
tokio::time::sleep(timeout_delay).await;
|
||||||
)
|
let stream_result = timeout(timeout_dur, TcpStream::connect(address)).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
let stream = match stream_result {
|
let stream = match stream_result {
|
||||||
Ok(Ok(s)) => s,
|
Ok(Ok(s)) => s,
|
||||||
@ -60,19 +76,29 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let client = Arc::new(Client::new(stream));
|
let client = Arc::new(Client::new(stream));
|
||||||
let message = Arc::new(args.spam_message.clone());
|
|
||||||
|
|
||||||
client.join_server(address, format!("BOT_{i}")).await;
|
client.join_server(address, format!("BOT_{i}")).await;
|
||||||
let join_handle = tokio::spawn(async move {
|
let cloned_args = args.clone();
|
||||||
loop {
|
let client_tick = client.clone();
|
||||||
if !client.process_packets().await {
|
let tick_handle = tokio::spawn(async move {
|
||||||
break;
|
let mut tick_interval = tokio::time::interval(Duration::from_millis(50));
|
||||||
}
|
tick_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||||
client.tick(message.clone(), args.spam_message_delay).await;
|
while !client_tick.closed.load(Ordering::Relaxed) {
|
||||||
|
tick_interval.tick().await;
|
||||||
|
client_tick.tick(&cloned_args).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
join_handles.push(join_handle);
|
|
||||||
log::info!("{}/{} Bots Joined", i + 1, args.count);
|
let client_proc = client.clone();
|
||||||
|
let proc_handle = tokio::spawn(async move {
|
||||||
|
while client_proc.process_packets().await {
|
||||||
|
// Keep reading and processing incoming packets without cancellation
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
join_handles.push(tick_handle);
|
||||||
|
join_handles.push(proc_handle);
|
||||||
|
log::info!("{}/{} Bots Joined", i + 1, bot_count);
|
||||||
}
|
}
|
||||||
// Graceful shutdown on Ctrl+C
|
// Graceful shutdown on Ctrl+C
|
||||||
tokio::signal::ctrl_c().await.unwrap();
|
tokio::signal::ctrl_c().await.unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user