Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
204ff4b850 | ||
|
|
655bc70eaa | ||
|
|
67de645f90 | ||
|
|
997c8b8294 | ||
|
|
f64b9d60d5 | ||
|
|
3498663c01 | ||
|
|
a073d75081 | ||
|
|
934cc22306 |
No files matched your search
@@ -1,14 +1,15 @@
|
|||||||
# Minecraft bot stress tester
|
# Minecraft bot stress tester
|
||||||
🤖 A simple open source app written in Java used for stress testing Minecraft servers with bots (fake players).
|
🤖 A simple open source app written in Java used for stress testing Minecraft servers with bots (fake players).
|
||||||
💥 It can be also used to test plugins or minigames.
|
💥 It can be also used to test plugins or minigames.
|
||||||
✔️ The MC version of the bots is 1.18 (supports 1.18.1).
|
✔️ The MC version of the bots is 1.18 (supports 1.18.2).
|
||||||
|
|
||||||
## 🆒 Features
|
## 🆒 Features
|
||||||
- ✅ Connect as many bots as you want
|
- ✅ Connect as many bots as you want
|
||||||
|
- ✅ Use SOCKS4 or SOCKS5 proxies from file or URL
|
||||||
- ✅ Receive colored or noncolored chat
|
- ✅ Receive colored or noncolored chat
|
||||||
- ✅ Set connection delay
|
- ✅ Set connection delay
|
||||||
- ✅ Set message or command on join
|
- ✅ Set message or command on join
|
||||||
- ✅ Generate random or real looking nicknames
|
- ✅ Generate random or real looking nicknames or load from file
|
||||||
|
|
||||||
## 📖 Usage
|
## 📖 Usage
|
||||||
Minimal Java version: 8
|
Minimal Java version: 8
|
||||||
@@ -17,7 +18,7 @@ Use of pre-compiled jar from [releases](https://github.com/crpmax/mc-bots/releas
|
|||||||
When running, you can write a chat message to the terminal to send it by all bots.
|
When running, you can write a chat message to the terminal to send it by all bots.
|
||||||
|
|
||||||
## 🧪 Example
|
## 🧪 Example
|
||||||
`java -jar mc-bots-1.0.0.jar -s 192.168.0.189:25565 -d 10 11 -r -p BOT_ -c 30`
|
`java -jar mc-bots-1.2.0.jar -s 192.168.0.189:25565 -d 10 11 -r -p BOT_ -c 30`
|
||||||
|
|
||||||
<img src="https://imgur.com/XWcckas.png" title="Connected bots" width="350px"/>
|
<img src="https://imgur.com/XWcckas.png" title="Connected bots" width="350px"/>
|
||||||
<img src="https://imgur.com/CvJq1Io.gif" title="Sending chat message by bots" width="350px"/>
|
<img src="https://imgur.com/CvJq1Io.gif" title="Sending chat message by bots" width="350px"/>
|
||||||
@@ -32,6 +33,9 @@ When running, you can write a chat message to the terminal to send it by all bot
|
|||||||
`-n` Do not use color in terminal - useful when the terminal does not support it
|
`-n` Do not use color in terminal - useful when the terminal does not support it
|
||||||
`-m` Minimal run - do not use any listeners, will not receive chat, useful for large amounts of bots
|
`-m` Minimal run - do not use any listeners, will not receive chat, useful for large amounts of bots
|
||||||
`-x` The most minimal run - No listeners, no control, no chat - useful for large amounts of bots for better performance
|
`-x` The most minimal run - No listeners, no control, no chat - useful for large amounts of bots for better performance
|
||||||
|
`-t` Set proxy type - SOCKS4 or SOCKS5
|
||||||
|
`-l` Set proxy list file
|
||||||
|
`-nicks` Set nicknames file
|
||||||
|
|
||||||
## ⚠ DISCLAIMER
|
## ⚠ DISCLAIMER
|
||||||
**This app is made for educational and testing purposes only.
|
**This app is made for educational and testing purposes only.
|
||||||
|
|||||||
+3
-2
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'me.creepermaxcz.mc-bots'
|
group 'me.creepermaxcz.mc-bots'
|
||||||
version '1.1.0'
|
version '1.2.2'
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
targetCompatibility = 1.8
|
targetCompatibility = 1.8
|
||||||
@@ -14,7 +14,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.Steveice10:MCProtocolLib:1.18-2'
|
implementation 'com.github.GeyserMC:MCProtocolLib:1.18.2-1'
|
||||||
implementation 'commons-cli:commons-cli:1.5.0'
|
implementation 'commons-cli:commons-cli:1.5.0'
|
||||||
implementation 'com.diogonunes:JColor:5.2.0'
|
implementation 'com.diogonunes:JColor:5.2.0'
|
||||||
implementation 'dnsjava:dnsjava:3.4.3'
|
implementation 'dnsjava:dnsjava:3.4.3'
|
||||||
@@ -24,6 +24,7 @@ dependencies {
|
|||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes 'Main-Class': 'me.creepermaxcz.mcbots.Main'
|
attributes 'Main-Class': 'me.creepermaxcz.mcbots.Main'
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
}
|
}
|
||||||
from {
|
from {
|
||||||
configurations.runtimeClasspath.filter{ it.exists() }.collect { it.isDirectory() ? it : zipTree(it) }
|
configurations.runtimeClasspath.filter{ it.exists() }.collect { it.isDirectory() ? it : zipTree(it) }
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class Bot extends Thread {
|
|||||||
|
|
||||||
Log.info("Creating bot", nickname);
|
Log.info("Creating bot", nickname);
|
||||||
protocol = new MinecraftProtocol(nickname);
|
protocol = new MinecraftProtocol(nickname);
|
||||||
client = new TcpClientSession(address.getHostString(), address.getPort(), protocol);
|
client = new TcpClientSession(address.getHostString(), address.getPort(), protocol, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ public class Log {
|
|||||||
log("INFO | ");
|
log("INFO | ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void warn(String ...in) {
|
||||||
|
log("WARN | " + String.join(" ", in));
|
||||||
|
}
|
||||||
|
public static void warn() {
|
||||||
|
log("WARN | ");
|
||||||
|
}
|
||||||
|
|
||||||
public static void chat(String ...in) {
|
public static void chat(String ...in) {
|
||||||
log("CHAT | " + String.join(" ", in));
|
log("CHAT | " + String.join(" ", in));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
package me.creepermaxcz.mcbots;
|
package me.creepermaxcz.mcbots;
|
||||||
|
|
||||||
|
import com.github.steveice10.packetlib.ProxyInfo;
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.xbill.DNS.Lookup;
|
import org.xbill.DNS.Lookup;
|
||||||
import org.xbill.DNS.Record;
|
import org.xbill.DNS.Record;
|
||||||
import org.xbill.DNS.SRVRecord;
|
import org.xbill.DNS.SRVRecord;
|
||||||
import org.xbill.DNS.Type;
|
import org.xbill.DNS.Type;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.io.*;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.*;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static boolean coloredChat = true;
|
public static boolean coloredChat = true;
|
||||||
@@ -26,6 +28,12 @@ public class Main {
|
|||||||
private static boolean mostMinimal = false;
|
private static boolean mostMinimal = false;
|
||||||
public static String joinMessage;
|
public static String joinMessage;
|
||||||
|
|
||||||
|
private static boolean useProxies = false;
|
||||||
|
private static final ArrayList<InetSocketAddress> proxies = new ArrayList<>();
|
||||||
|
private static int proxyIndex = 0;
|
||||||
|
private static int proxyCount = 0;
|
||||||
|
private static ProxyInfo.Type proxyType;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
@@ -47,6 +55,11 @@ public class Main {
|
|||||||
options.addOption("x", "most-minimal", false, "minimal run without any control, just connect the bots");
|
options.addOption("x", "most-minimal", false, "minimal run without any control, just connect the bots");
|
||||||
options.addOption("j", "join-msg", true, "join message / command");
|
options.addOption("j", "join-msg", true, "join message / command");
|
||||||
|
|
||||||
|
options.addOption("l", "proxy-list", true, "Path or URL to proxy list file with proxy:port on every line");
|
||||||
|
options.addOption("t", "proxy-type", true, "Proxy type: SOCKS4 or SOCKS5");
|
||||||
|
|
||||||
|
options.addOption("nicks", true, "Path to nicks file with nick on every line");
|
||||||
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
CommandLineParser parser = new DefaultParser();
|
||||||
CommandLine cmd = null;
|
CommandLine cmd = null;
|
||||||
|
|
||||||
@@ -58,6 +71,76 @@ public class Main {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd.hasOption('t') && cmd.hasOption('l')) {
|
||||||
|
String typeStr = cmd.getOptionValue('t').toUpperCase();
|
||||||
|
|
||||||
|
//get proxy type
|
||||||
|
try {
|
||||||
|
proxyType = ProxyInfo.Type.valueOf(typeStr);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.error("Inavlid proxy type, use SOCKS4 or SOCKS5.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String proxyPath = cmd.getOptionValue("l");
|
||||||
|
|
||||||
|
//read proxy list file
|
||||||
|
try {
|
||||||
|
|
||||||
|
try {
|
||||||
|
//try to read specified path as URL
|
||||||
|
URL url = new URL(proxyPath);
|
||||||
|
|
||||||
|
BufferedReader read = new BufferedReader(
|
||||||
|
new InputStreamReader(url.openStream()));
|
||||||
|
|
||||||
|
Log.info("Reading proxies from URL");
|
||||||
|
String line;
|
||||||
|
while ((line = read.readLine()) != null) {
|
||||||
|
try {
|
||||||
|
String[] parts = line.trim().split(":");
|
||||||
|
if (parts.length == 2) {
|
||||||
|
int port = Integer.parseInt(parts[1]);
|
||||||
|
proxies.add(new InetSocketAddress(parts[0], port));
|
||||||
|
proxyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored) { }
|
||||||
|
}
|
||||||
|
read.close();
|
||||||
|
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
Log.info("Specified proxy file is not a URL, trying to read file");
|
||||||
|
|
||||||
|
Scanner scanner = new Scanner(new File(proxyPath));
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
try {
|
||||||
|
String[] parts = scanner.nextLine().trim().split(":");
|
||||||
|
if (parts.length == 2) {
|
||||||
|
int port = Integer.parseInt(parts[1]);
|
||||||
|
proxies.add(new InetSocketAddress(parts[0], port));
|
||||||
|
proxyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored) { }
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.error("Invalid proxy list file path.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proxyCount > 0) {
|
||||||
|
useProxies = true;
|
||||||
|
Log.info("Loaded " + proxyCount + " valid proxies");
|
||||||
|
} else {
|
||||||
|
Log.error("No valid proxies loaded");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
botCount = Integer.parseInt(cmd.getOptionValue('c', "1"));
|
botCount = Integer.parseInt(cmd.getOptionValue('c', "1"));
|
||||||
|
|
||||||
@@ -104,7 +187,18 @@ public class Main {
|
|||||||
boolean realNicknames = cmd.hasOption('r');
|
boolean realNicknames = cmd.hasOption('r');
|
||||||
|
|
||||||
NickGenerator nickGen = new NickGenerator();
|
NickGenerator nickGen = new NickGenerator();
|
||||||
nickGen.setReal(realNicknames);
|
|
||||||
|
if (cmd.hasOption("nicks")) {
|
||||||
|
Log.info("Loading nicknames from specified file");
|
||||||
|
int nicksCount = nickGen.loadFromFile(cmd.getOptionValue("nicks"));
|
||||||
|
if (nicksCount < botCount) {
|
||||||
|
Log.warn("Nickname count is lower than bot count!");
|
||||||
|
Thread.sleep(3000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nickGen.setReal(realNicknames);
|
||||||
|
}
|
||||||
|
|
||||||
nickGen.setPrefix(cmd.getOptionValue('p', ""));
|
nickGen.setPrefix(cmd.getOptionValue('p', ""));
|
||||||
|
|
||||||
InetSocketAddress inetAddr = new InetSocketAddress(
|
InetSocketAddress inetAddr = new InetSocketAddress(
|
||||||
@@ -119,10 +213,35 @@ public class Main {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
for (int i = 0; i < botCount; i++) {
|
for (int i = 0; i < botCount; i++) {
|
||||||
try {
|
try {
|
||||||
|
ProxyInfo proxyInfo = null;
|
||||||
|
if (useProxies) {
|
||||||
|
InetSocketAddress proxySocket = proxies.get(proxyIndex);
|
||||||
|
|
||||||
|
if (!minimal) {
|
||||||
|
Log.info(
|
||||||
|
"Using proxy: (" + proxyIndex + ")",
|
||||||
|
proxySocket.getHostString() + ":" + proxySocket.getPort()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyInfo = new ProxyInfo(
|
||||||
|
proxyType,
|
||||||
|
proxySocket
|
||||||
|
);
|
||||||
|
|
||||||
|
//increment or reset current proxy index
|
||||||
|
if (proxyIndex < (proxyCount - 1)) {
|
||||||
|
proxyIndex++;
|
||||||
|
} else {
|
||||||
|
proxyIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Bot bot = new Bot(
|
Bot bot = new Bot(
|
||||||
nickGen.nextNick(),
|
nickGen.nextNick(),
|
||||||
inetAddr,
|
inetAddr,
|
||||||
null
|
proxyInfo
|
||||||
);
|
);
|
||||||
bot.start();
|
bot.start();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package me.creepermaxcz.mcbots;
|
package me.creepermaxcz.mcbots;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.InputStream;
|
import java.net.InetSocketAddress;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NickGenerator {
|
public class NickGenerator {
|
||||||
@@ -25,6 +26,34 @@ public class NickGenerator {
|
|||||||
|
|
||||||
private String prefix = "";
|
private String prefix = "";
|
||||||
|
|
||||||
|
public int loadFromFile(String filePath) {
|
||||||
|
lines = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Scanner scanner = new Scanner(new File(filePath));
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
try {
|
||||||
|
String line = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
//add only valid nicknames
|
||||||
|
if (line.matches("^[a-zA-Z0-9_]{3,16}$")) {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored) { }
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.error("Invalid nicknames list file path");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
linesSize = lines.size();
|
||||||
|
Log.info("Loaded " + linesSize + " valid nicknames");
|
||||||
|
real = true;
|
||||||
|
|
||||||
|
return linesSize;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadLines() {
|
private void loadLines() {
|
||||||
try (InputStream resource = getClass().getResourceAsStream("/files/nicks.txt")) {
|
try (InputStream resource = getClass().getResourceAsStream("/files/nicks.txt")) {
|
||||||
lines = new BufferedReader(
|
lines = new BufferedReader(
|
||||||
|
|||||||
Reference in new issue
Block a user