fixing format issue and adding -rm parameter for excutring random moves on bots each ms #112
No files matched your search
@@ -13,6 +13,8 @@ For older MC versions please look in the [releases](https://github.com/crpmax/mc
|
|||||||
- ✅ Generate random or real looking nicknames or load from file
|
- ✅ Generate random or real looking nicknames or load from file
|
||||||
- ✅ Online (premium) account support with login using Microsoft
|
- ✅ Online (premium) account support with login using Microsoft
|
||||||
- ✅ Control all or selected bots
|
- ✅ Control all or selected bots
|
||||||
|
- ✅ Random movement simulation
|
||||||
|
- ✅ Automatic jump when stuck (obstacle avoidance)
|
||||||
|
|
||||||
## 📖 Usage
|
## 📖 Usage
|
||||||
Minimal Java version: 17
|
Minimal Java version: 17
|
||||||
@@ -24,6 +26,9 @@ When running, you can write a chat message to the terminal to send it by all bot
|
|||||||
`java -jar mc-bots-1.2.17.jar -s 192.168.0.189:25565 -p BOT_ -d 4000 5000 -c 30 -r`
|
`java -jar mc-bots-1.2.17.jar -s 192.168.0.189:25565 -p BOT_ -d 4000 5000 -c 30 -r`
|
||||||
This will connect 30 bots to server at 192.168.0.189:25565 with delay 4000-5000 ms and will use real-looking nicknames prefixed with BOT_
|
This will connect 30 bots to server at 192.168.0.189:25565 with delay 4000-5000 ms and will use real-looking nicknames prefixed with BOT_
|
||||||
|
|
||||||
|
`java -jar mc-bots-1.2.17.jar -s 192.168.0.189:25565 -c 10 -rm 1000 -g`
|
||||||
|
This will connect 10 bots with random movement every 1 second and gravity simulation enabled
|
||||||
|
|
||||||
<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"/>
|
||||||
|
|
||||||
@@ -42,6 +47,7 @@ This will connect 30 bots to server at 192.168.0.189:25565 with delay 4000-5000
|
|||||||
`-g` Try to simulate gravity by falling down
|
`-g` Try to simulate gravity by falling down
|
||||||
`-o` Use online (premium) account (login with Microsoft OAuth)
|
`-o` Use online (premium) account (login with Microsoft OAuth)
|
||||||
`-ar <delay>` Set auto-respawn delay (default is 100 ms, set to -1 to disable)
|
`-ar <delay>` Set auto-respawn delay (default is 100 ms, set to -1 to disable)
|
||||||
|
`-rm <interval>` Enable random movement with specified interval in milliseconds (e.g. 2000 for 2 seconds)
|
||||||
`--nicks <file>` Set nicknames file
|
`--nicks <file>` Set nicknames file
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +56,7 @@ Commands can be typed to the console. They are prefixed with `.` or `!`.
|
|||||||
Without the prefix it will be sent as a chat message!
|
Without the prefix it will be sent as a chat message!
|
||||||
`.list` or `.ls` - list all connected bots
|
`.list` or `.ls` - list all connected bots
|
||||||
`.control <nick>` or `.ctrl <nick>` - select one or multiple bots to control
|
`.control <nick>` or `.ctrl <nick>` - select one or multiple bots to control
|
||||||
|
`.jump` or `.j` - make bots jump (useful for testing or getting unstuck)
|
||||||
`.exit <limit>` or `.leave <limit>` - disconnect all or specified number of bots
|
`.exit <limit>` or `.leave <limit>` - disconnect all or specified number of bots
|
||||||
|
|
||||||
## ⚠ DISCLAIMER
|
## ⚠ DISCLAIMER
|
||||||
|
|||||||
@@ -42,6 +42,17 @@ public class Bot extends Thread {
|
|||||||
private boolean connected;
|
private boolean connected;
|
||||||
|
|
||||||
private boolean manualDisconnecting = false;
|
private boolean manualDisconnecting = false;
|
||||||
|
|
||||||
|
private Random movementRandom = new Random();
|
||||||
|
private double currentDirection = 0;
|
||||||
|
private int stepsInDirection = 0;
|
||||||
|
private int maxStepsInDirection = 0;
|
||||||
|
|
||||||
|
// Stuck detection variables
|
||||||
|
private double previousX = 0, previousZ = 0;
|
||||||
|
private int stuckCounter = 0;
|
||||||
|
private static final int STUCK_THRESHOLD = 3; // Consider stuck after 3 failed moves
|
||||||
|
private static final double MIN_MOVEMENT = 0.01; // Minimum movement to not be considered stuck
|
||||||
|
|
||||||
public Bot(MinecraftProtocol protocol, InetSocketAddress address, ProxyInfo proxy) {
|
public Bot(MinecraftProtocol protocol, InetSocketAddress address, ProxyInfo proxy) {
|
||||||
this.nickname = protocol.getProfile().getName();
|
this.nickname = protocol.getProfile().getName();
|
||||||
@@ -204,6 +215,69 @@ public class Bot extends Thread {
|
|||||||
{
|
{
|
||||||
client.send(new ServerboundMovePlayerPosPacket(true, false, x, y, z));
|
client.send(new ServerboundMovePlayerPosPacket(true, false, x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void jump() {
|
||||||
|
if (connected && lastZ != -1) {
|
||||||
|
// Jump by moving up 0.42 blocks (standard Minecraft jump height)
|
||||||
|
move(0, 0.42, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceJump() {
|
||||||
|
// Force jump regardless of stuck status (for manual testing)
|
||||||
|
jump();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveRandomly() {
|
||||||
|
if (connected && lastZ != -1) {
|
||||||
|
// Store current position before moving
|
||||||
|
double currentX = lastX;
|
||||||
|
double currentZ = lastZ;
|
||||||
|
|
||||||
|
// Check if we need a new direction
|
||||||
|
if (stepsInDirection >= maxStepsInDirection) {
|
||||||
|
// Pick new random direction and duration
|
||||||
|
currentDirection = movementRandom.nextDouble() * 2 * Math.PI;
|
||||||
|
maxStepsInDirection = 3 + movementRandom.nextInt(8); // Walk 3-10 steps in one direction
|
||||||
|
stepsInDirection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move in current direction with realistic speed
|
||||||
|
double speed = 0.08; // About 0.08 blocks per movement call
|
||||||
|
double deltaX = Math.cos(currentDirection) * speed;
|
||||||
|
double deltaZ = Math.sin(currentDirection) * speed;
|
||||||
|
|
||||||
|
// Occasionally stop moving (simulate standing still)
|
||||||
|
if (movementRandom.nextDouble() < 0.1) {
|
||||||
|
deltaX = 0;
|
||||||
|
deltaZ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bot is stuck (hasn't moved much from previous position)
|
||||||
|
double distanceMoved = Math.sqrt(Math.pow(currentX - previousX, 2) + Math.pow(currentZ - previousZ, 2));
|
||||||
|
|
||||||
|
if (distanceMoved < MIN_MOVEMENT && (deltaX != 0 || deltaZ != 0)) {
|
||||||
|
stuckCounter++;
|
||||||
|
|
||||||
|
if (stuckCounter >= STUCK_THRESHOLD) {
|
||||||
|
// Bot is stuck, try jumping and change direction
|
||||||
|
jump();
|
||||||
|
currentDirection = movementRandom.nextDouble() * 2 * Math.PI; // New random direction
|
||||||
|
stuckCounter = 0; // Reset stuck counter
|
||||||
|
stepsInDirection = maxStepsInDirection; // Force new direction next time
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stuckCounter = 0; // Reset if bot moved successfully
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store position for next stuck check
|
||||||
|
previousX = currentX;
|
||||||
|
previousZ = currentZ;
|
||||||
|
|
||||||
|
move(deltaX, 0, deltaZ);
|
||||||
|
stepsInDirection++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return connected;
|
return connected;
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ public class Main {
|
|||||||
options.addOption("o", "online", false, "Use online mode (premium) account");
|
options.addOption("o", "online", false, "Use online mode (premium) account");
|
||||||
|
|
||||||
options.addOption("ar", "auto-respawn", true, "Set autorespawn delay (-1 to disable)");
|
options.addOption("ar", "auto-respawn", true, "Set autorespawn delay (-1 to disable)");
|
||||||
|
|
||||||
|
options.addOption("rm", "random-move", true, "Enable random movement with interval in ms (e.g. 2000)");
|
||||||
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
CommandLineParser parser = new DefaultParser();
|
||||||
CommandLine cmd = null;
|
CommandLine cmd = null;
|
||||||
@@ -369,6 +371,19 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}, 1000L, 500L);
|
}, 1000L, 500L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//random movement timer
|
||||||
|
if (cmd.hasOption("rm")) {
|
||||||
|
long moveInterval = Long.parseLong(cmd.getOptionValue("rm", "2000"));
|
||||||
|
Log.info("Random movement enabled with interval: " + moveInterval + "ms");
|
||||||
|
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
bots.forEach(Bot::moveRandomly);
|
||||||
|
}
|
||||||
|
}, moveInterval, moveInterval);
|
||||||
|
}
|
||||||
|
|
||||||
Terminal terminal = TerminalBuilder.builder().build();
|
Terminal terminal = TerminalBuilder.builder().build();
|
||||||
LineReader lineReader = LineReaderBuilder.builder()
|
LineReader lineReader = LineReaderBuilder.builder()
|
||||||
@@ -451,6 +466,15 @@ public class Main {
|
|||||||
for (Bot bot : bots) {
|
for (Bot bot : bots) {
|
||||||
Log.info(bot.getNickname(), bot.hasMainListener() ? "[MainListener]" : "");
|
Log.info(bot.getNickname(), bot.hasMainListener() ? "[MainListener]" : "");
|
||||||
}
|
}
|
||||||
|
} else if (commandName.equalsIgnoreCase("jump") || commandName.equalsIgnoreCase("j")) {
|
||||||
|
// Jump command for testing
|
||||||
|
if (!controlledBots.isEmpty()) {
|
||||||
|
Log.info("Making controlled bots jump");
|
||||||
|
controlledBots.forEach(Bot::forceJump);
|
||||||
|
} else {
|
||||||
|
Log.info("Making all bots jump");
|
||||||
|
bots.forEach(Bot::forceJump);
|
||||||
|
}
|
||||||
} else if (commandName.equalsIgnoreCase("leave") || commandName.equalsIgnoreCase("exit")) {
|
} else if (commandName.equalsIgnoreCase("leave") || commandName.equalsIgnoreCase("exit")) {
|
||||||
int limit = -1;
|
int limit = -1;
|
||||||
if (split.length >= 2) {
|
if (split.length >= 2) {
|
||||||
|
|||||||
@@ -152,8 +152,11 @@ public class Utils {
|
|||||||
Component component = arg.asComponent();
|
Component component = arg.asComponent();
|
||||||
if (component instanceof TranslatableComponent) {
|
if (component instanceof TranslatableComponent) {
|
||||||
placeholders.add(translate((TranslatableComponent) component));
|
placeholders.add(translate((TranslatableComponent) component));
|
||||||
} else if (arg instanceof TextComponent) {
|
} else if (component instanceof TextComponent) {
|
||||||
placeholders.add(getFullText((TextComponent) component, false));
|
placeholders.add(getFullText((TextComponent) component, false));
|
||||||
|
} else {
|
||||||
|
// Fallback for other component types
|
||||||
|
placeholders.add(component.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Log.info(Arrays.toString(placeholders.toArray()));
|
//Log.info(Arrays.toString(placeholders.toArray()));
|
||||||
@@ -161,6 +164,8 @@ public class Utils {
|
|||||||
return String.format(base, placeholders.toArray());
|
return String.format(base, placeholders.toArray());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.error("Error formatting '"+base+"' with placeholders " + Arrays.toString(placeholders.toArray()));
|
Log.error("Error formatting '"+base+"' with placeholders " + Arrays.toString(placeholders.toArray()));
|
||||||
|
// Return a fallback message instead of empty string
|
||||||
|
return key + " " + String.join(" ", placeholders);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return base;
|
return base;
|
||||||
|
|||||||
Reference in new issue
Block a user