16 Commits
Author SHA1 Message Date
Lncvrt 939ed09bdb Merge pull request 'Update to Minecraft 1.21.11' (#117) from eripum9/main into main
Reviewed-on: http://git.lncvrt.xyz/Lncvrt/mc-bots/pulls/117
2026-03-28 18:20:17 +00:00
eripum9 8417fd7b45 chore: bump version to 1.2.18 2026-02-11 20:02:47 +01:00
eripum9 60e624e47e feat: update dependencies for Minecraft 1.21.11 and Java 21 support 2026-02-11 19:58:00 +01:00
crpmax e8e3b4f9c0 fix: downgrade dnsjava, feat: update readme 2025-08-06 14:35:49 +02:00
정현우니andCreeperMax 7c1eb7cf24 Update for Minecraft 1.21.7 (#111)
* chore: update Gradle wrapper to version 9.0.0

* chore: update Java toolchain to version 17

* chore: update dependencies to latest versions

* fix: add missing parameter to chat packet constructor

---------

Co-authored-by: CreeperMax <38163280+crpmax@users.noreply.github.com>
2025-08-06 14:11:38 +02:00
crpmax ce9d01a47f feat: v1.2.16 - update to MC 1.21.6 2025-08-06 13:55:45 +02:00
crpmax b81b61187f feat: v1.2.15 - update to MC 1.21.5 2025-08-06 13:47:38 +02:00
crpmax 5e109efc56 chore: update readme 2025-02-26 21:13:34 +01:00
crpmax 91e4d44170 chore: remove unused constructor 2025-02-26 21:12:04 +01:00
Elecast e2a477e56f update to MC 1.21.4 (#101) 2025-02-26 19:48:04 +01:00
crpmax 1ce1f98801 update to MC 1.21.2 2025-02-21 18:59:54 +01:00
crpmax 9865364919 update to MC 1.21 2025-02-21 18:12:04 +01:00
Lukas ʕ•́ᴥ•̀ʔ fb99076f88 Fix spelling mistake (#93) 2024-07-24 13:56:46 +02:00
crpmax da26ec8a99 update readme to MC 1.20.6 2024-07-17 00:59:12 +02:00
crpmax 8b2dddf8d6 update to MC 1.20.6 2024-07-17 00:58:23 +02:00
crpmax c57febbd0a update to MC 1.20.4 2024-07-17 00:15:52 +02:00
9 changed files with 5823 additions and 4631 deletions

No files matched your search

+4 -4
View File
@@ -1,7 +1,7 @@
# 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.20 (supports 1.20.x). ✔️ The MC version of the bots is 1.21.7 (supports server 1.21.8)
For older MC versions please look in the [releases](https://github.com/crpmax/mc-bots/releases " releases"). For older MC versions please look in the [releases](https://github.com/crpmax/mc-bots/releases " releases").
## 🆒 Features ## 🆒 Features
@@ -11,17 +11,17 @@ For older MC versions please look in the [releases](https://github.com/crpmax/mc
- ✅ Set connection delay - ✅ Set connection delay
- ✅ Set messages or commands on join - ✅ Set messages or commands on join
- ✅ 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 OAuth - only for migrated accounts - ✅ Online (premium) account support with login using Microsoft
- ✅ Control all or selected bots - ✅ Control all or selected bots
## 📖 Usage ## 📖 Usage
Minimal Java version: 8 Minimal Java version: 17
Use of pre-compiled jar from [releases](https://github.com/crpmax/mc-bots/releases " releases"): Use of pre-compiled jar from [releases](https://github.com/crpmax/mc-bots/releases " releases"):
`java -jar mc-bots.jar -s <server address> [arguments]` `java -jar mc-bots.jar -s <server address> [arguments]`
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.2.6.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_
<img src="https://imgur.com/XWcckas.png" title="Connected bots" width="350px"/> <img src="https://imgur.com/XWcckas.png" title="Connected bots" width="350px"/>
+14 -10
View File
@@ -3,10 +3,7 @@ plugins {
} }
group 'me.creepermaxcz.mc-bots' group 'me.creepermaxcz.mc-bots'
version '1.2.9' version '1.2.18'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories { repositories {
mavenCentral() mavenCentral()
@@ -16,12 +13,19 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.github.steveice10:mcprotocollib:1.20.2-1' implementation 'org.geysermc.mcprotocollib:protocol:1.21.11-SNAPSHOT'
implementation 'commons-cli:commons-cli:1.5.0' implementation 'net.kyori:adventure-text-serializer-gson:4.24.0'
implementation 'com.diogonunes:JColor:5.2.0' implementation 'commons-cli:commons-cli:1.9.0'
implementation 'dnsjava:dnsjava:3.4.3' implementation 'com.diogonunes:JColor:5.5.1'
implementation 'org.slf4j:slf4j-nop:1.7.32' implementation 'dnsjava:dnsjava:3.5.3'
implementation 'org.jline:jline:3.22.0' implementation 'org.slf4j:slf4j-nop:2.0.17'
implementation 'org.jline:jline:3.30.4'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
} }
jar { jar {
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+38 -53
View File
@@ -1,25 +1,27 @@
package me.creepermaxcz.mcbots; package me.creepermaxcz.mcbots;
import com.github.steveice10.mc.auth.service.AuthenticationService; import org.geysermc.mcprotocollib.auth.GameProfile;
import com.github.steveice10.mc.auth.service.SessionService; import org.geysermc.mcprotocollib.auth.SessionService;
import com.github.steveice10.mc.protocol.MinecraftConstants; import org.geysermc.mcprotocollib.network.ClientSession;
import com.github.steveice10.mc.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.network.ProxyInfo;
import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException; import org.geysermc.mcprotocollib.network.Session;
import com.github.steveice10.mc.protocol.data.game.ClientCommand; import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundLoginPacket; import org.geysermc.mcprotocollib.network.event.session.SessionAdapter;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerCombatKillPacket; import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket; import org.geysermc.mcprotocollib.network.packet.Packet;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket; import org.geysermc.mcprotocollib.network.session.ClientNetworkSession;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket; import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; import org.geysermc.mcprotocollib.protocol.data.UnexpectedEncryptionException;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.*; import org.geysermc.mcprotocollib.protocol.data.game.ClientCommand;
import com.github.steveice10.packetlib.ProxyInfo; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLoginPacket;
import com.github.steveice10.packetlib.Session; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerCombatKillPacket;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket;
import com.github.steveice10.packetlib.event.session.SessionAdapter; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket;
import com.github.steveice10.packetlib.packet.Packet; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket;
import com.github.steveice10.packetlib.tcp.TcpClientSession; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.*;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.time.Instant; import java.time.Instant;
@@ -29,12 +31,10 @@ import java.util.regex.Pattern;
public class Bot extends Thread { public class Bot extends Thread {
MinecraftProtocol protocol = null;
private String nickname; private String nickname;
private ProxyInfo proxy; private ProxyInfo proxy;
private InetSocketAddress address; private InetSocketAddress address;
private Session client; private ClientSession client;
private boolean hasMainListener; private boolean hasMainListener;
private double lastX, lastY, lastZ = -1; private double lastX, lastY, lastZ = -1;
@@ -43,25 +43,18 @@ public class Bot extends Thread {
private boolean manualDisconnecting = false; private boolean manualDisconnecting = false;
public Bot(String nickname, InetSocketAddress address, ProxyInfo proxy) { public Bot(MinecraftProtocol protocol, InetSocketAddress address, ProxyInfo proxy) {
this.nickname = nickname; this.nickname = protocol.getProfile().getName();
this.address = address; this.address = address;
this.proxy = proxy; this.proxy = proxy;
Log.info("Creating bot", nickname); Log.info("Creating bot", nickname);
protocol = new MinecraftProtocol(nickname);
client = new TcpClientSession(address.getHostString(), address.getPort(), protocol, proxy);
}
public Bot(AuthenticationService authService, InetSocketAddress address, ProxyInfo proxy) { client = ClientNetworkSessionFactory.factory()
this.nickname = authService.getUsername(); .setAddress(address.getHostString(), address.getPort())
this.address = address; .setProtocol(protocol)
this.proxy = proxy; .setProxy(proxy)
.create();
Log.info("Creating bot", nickname);
protocol = new MinecraftProtocol(authService.getSelectedProfile(), authService.getAccessToken());
client = new TcpClientSession(address.getHostString(), address.getPort(), protocol, proxy);
SessionService sessionService = new SessionService(); SessionService sessionService = new SessionService();
client.setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService); client.setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService);
@@ -93,11 +86,11 @@ public class Bot extends Thread {
else if (packet instanceof ClientboundPlayerPositionPacket) { else if (packet instanceof ClientboundPlayerPositionPacket) {
ClientboundPlayerPositionPacket p = (ClientboundPlayerPositionPacket) packet; ClientboundPlayerPositionPacket p = (ClientboundPlayerPositionPacket) packet;
lastX = p.getX(); lastX = p.getPosition().getX();
lastY = p.getY(); lastY = p.getPosition().getY();
lastZ = p.getZ(); lastZ = p.getPosition().getZ();
client.send(new ServerboundAcceptTeleportationPacket(p.getTeleportId())); client.send(new ServerboundAcceptTeleportationPacket(p.getId()));
} }
else if (packet instanceof ClientboundPlayerCombatKillPacket){ else if (packet instanceof ClientboundPlayerCombatKillPacket){
if (Main.autoRespawnDelay >= 0) { if (Main.autoRespawnDelay >= 0) {
@@ -155,19 +148,10 @@ public class Bot extends Thread {
} }
public void sendChat(String text) { public void sendChat(String text) {
// timeStamp will provide when this message was sent by the user. If this value was not set or was set to 0,
// The server console will print out that the message was "expired". To avoid this, set timeStamp as now.
long timeStamp = Instant.now().toEpochMilli();
// Send command // Send command
if (text.startsWith("/")) { if (text.startsWith("/")) {
client.send(new ServerboundChatCommandPacket( client.send(new ServerboundChatCommandPacket(
text.substring(1), text.substring(1)
timeStamp,
0L,
Collections.emptyList(),
0,
new BitSet()
)); ));
} else { } else {
// Send chat message // Send chat message
@@ -181,7 +165,8 @@ public class Bot extends Thread {
0L, 0L,
null, null,
0, 0,
new BitSet() new BitSet(),
0
)); ));
} }
} }
@@ -217,7 +202,7 @@ public class Bot extends Thread {
public void moveTo(double x, double y, double z) public void moveTo(double x, double y, double z)
{ {
client.send(new ServerboundMovePlayerPosPacket(true, x, y, z)); client.send(new ServerboundMovePlayerPosPacket(true, false, x, y, z));
} }
public boolean isConnected() { public boolean isConnected() {
+27 -50
View File
@@ -1,11 +1,15 @@
package me.creepermaxcz.mcbots; package me.creepermaxcz.mcbots;
import com.github.steveice10.mc.auth.exception.request.AuthPendingException; import net.lenni0451.commons.httpclient.HttpClient;
import com.github.steveice10.mc.auth.service.AuthenticationService; import net.raphimc.minecraftauth.MinecraftAuth;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService; import net.raphimc.minecraftauth.java.JavaAuthManager;
import com.github.steveice10.mc.auth.util.HTTP; import net.raphimc.minecraftauth.java.model.MinecraftProfile;
import com.github.steveice10.mc.protocol.data.status.ServerStatusInfo; import net.raphimc.minecraftauth.msa.model.MsaDeviceCode;
import com.github.steveice10.packetlib.ProxyInfo; import net.raphimc.minecraftauth.msa.service.impl.DeviceCodeMsaAuthService;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.network.ProxyInfo;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import org.geysermc.mcprotocollib.protocol.data.status.ServerStatusInfo;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.jline.reader.LineReader; import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder; import org.jline.reader.LineReaderBuilder;
@@ -21,6 +25,7 @@ import java.io.*;
import java.net.*; import java.net.*;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -105,7 +110,7 @@ public class Main {
try { try {
proxyType = ProxyInfo.Type.valueOf(typeStr); proxyType = ProxyInfo.Type.valueOf(typeStr);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Log.error("Inavlid proxy type, use SOCKS4 or SOCKS5."); Log.error("Invalid proxy type, use SOCKS4 or SOCKS5.");
System.exit(1); System.exit(1);
} }
@@ -269,55 +274,27 @@ public class Main {
Log.warn("There was an error retrieving server status information. The server may be offline or running on a different version."); Log.warn("There was an error retrieving server status information. The server may be offline or running on a different version.");
} }
AuthenticationService authService = null; MinecraftProtocol protocol;
if (cmd.hasOption("o")) { if (cmd.hasOption("o")) {
Log.warn("Online mode enabled. The bot count will be set to 1."); Log.warn("Online mode enabled. The bot count will be set to 1.");
botCount = 1; botCount = 1;
// Create request parameters map HttpClient httpClient = MinecraftAuth.createHttpClient();
Map<String, String> params = new HashMap<>(); JavaAuthManager authManager = JavaAuthManager.create(httpClient)
params.put("client_id", CLIENT_ID); .login(DeviceCodeMsaAuthService::new, (Consumer<MsaDeviceCode>) msaDeviceCode -> {
params.put("scope", "XboxLive.signin"); Log.info("Authorize your Microsoft account on " + msaDeviceCode.getDirectVerificationUri());
Log.info("Waiting for authorization.");
});
// Send request to Microsoft OAuth api MinecraftProfile mcProfile = authManager.getMinecraftProfile().getUpToDate();
// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code Log.info("Logged in with username: " + mcProfile.getName());
URI endpointURI = URI.create("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode");
MsaAuthenticationService.MsCodeResponse response = HTTP.makeRequestForm(
Proxy.NO_PROXY, endpointURI, params, MsaAuthenticationService.MsCodeResponse.class
);
try {
Log.info("Please go to " + response.verification_uri.toURL() + " to authenticate your account - Code: " + response.user_code);
} catch (MalformedURLException e) {
Log.error("Error while trying to get the url of the authentication page");
}
authService = new MsaAuthenticationService(CLIENT_ID, response.device_code); GameProfile gameProfile = new GameProfile(mcProfile.getId(), mcProfile.getName());
protocol = new MinecraftProtocol(gameProfile, authManager.getMinecraftToken().getUpToDate().getToken());
// Wait for user to login on microsoft page
int retryMax = 20;
while (true) {
try {
authService.login();
break;
} catch (Exception e) {
if (e instanceof AuthPendingException) {
Log.info("Authentication is pending, waiting for user to authenticate...");
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {
}
if (retryMax == 0)
throw e;
retryMax--;
} else { } else {
throw e; protocol = null;
} }
}
}
}
AuthenticationService finalAuthService = authService;
new Thread(() -> { new Thread(() -> {
for (int i = 0; i < botCount; i++) { for (int i = 0; i < botCount; i++) {
try { try {
@@ -347,15 +324,15 @@ public class Main {
} }
Bot bot = null; Bot bot = null;
if (finalAuthService != null) { if (protocol != null) {
bot = new Bot( bot = new Bot(
finalAuthService, protocol,
inetAddr, inetAddr,
proxyInfo proxyInfo
); );
} else { } else {
bot = new Bot( bot = new Bot(
nickGen.nextNick(), new MinecraftProtocol(nickGen.nextNick()),
inetAddr, inetAddr,
proxyInfo proxyInfo
); );
@@ -1,10 +1,10 @@
package me.creepermaxcz.mcbots; package me.creepermaxcz.mcbots;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket; import org.geysermc.mcprotocollib.network.Session;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; import org.geysermc.mcprotocollib.network.event.session.*;
import com.github.steveice10.packetlib.Session; import org.geysermc.mcprotocollib.network.packet.Packet;
import com.github.steveice10.packetlib.event.session.*; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket;
import com.github.steveice10.packetlib.packet.Packet; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;
@@ -1,21 +1,23 @@
package me.creepermaxcz.mcbots; package me.creepermaxcz.mcbots;
import com.github.steveice10.mc.protocol.MinecraftConstants; import org.geysermc.mcprotocollib.network.ClientSession;
import com.github.steveice10.mc.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.network.Session;
import com.github.steveice10.mc.protocol.data.status.ServerStatusInfo; import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
import com.github.steveice10.mc.protocol.data.status.handler.ServerInfoHandler; import org.geysermc.mcprotocollib.network.event.session.SessionAdapter;
import com.github.steveice10.mc.protocol.data.status.handler.ServerPingTimeHandler; import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory;
import com.github.steveice10.packetlib.Session; import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import com.github.steveice10.packetlib.event.session.SessionAdapter; import org.geysermc.mcprotocollib.protocol.data.status.ServerStatusInfo;
import com.github.steveice10.packetlib.tcp.TcpClientSession; import org.geysermc.mcprotocollib.protocol.data.status.handler.ServerInfoHandler;
import org.geysermc.mcprotocollib.protocol.data.status.handler.ServerPingTimeHandler;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
public class ServerInfo { public class ServerInfo {
private final Session client; private final ClientSession client;
private ServerStatusInfo serverStatusInfo; private ServerStatusInfo serverStatusInfo;
private long ping; private long ping;
private boolean done; private boolean done;
@@ -23,7 +25,11 @@ public class ServerInfo {
public ServerInfo(InetSocketAddress address) { public ServerInfo(InetSocketAddress address) {
MinecraftProtocol protocol = new MinecraftProtocol(); MinecraftProtocol protocol = new MinecraftProtocol();
client = new TcpClientSession(address.getHostString(), address.getPort(), protocol, null);
client = ClientNetworkSessionFactory.factory()
.setAddress(address.getHostString(), address.getPort())
.setProtocol(protocol)
.create();
client.setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, (ServerInfoHandler) (session, info) -> { client.setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, (ServerInfoHandler) (session, info) -> {
this.serverStatusInfo = info; this.serverStatusInfo = info;
@@ -7,6 +7,7 @@ import com.google.gson.JsonParser;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.format.TextDecoration;
@@ -15,6 +16,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -121,9 +123,13 @@ public class Utils {
} }
public static String translate(TranslatableComponent message) { public static String translate(TranslatableComponent message) {
try {
if (lang == null) { if (lang == null) {
try { try {
InputStream resource = Utils.class.getResourceAsStream("/files/lang.json"); InputStream resource = Utils.class.getResourceAsStream("/files/lang.json");
if (resource == null) {
throw new Exception("Failed to load lang resource.");
}
lang = new JsonParser().parse(new InputStreamReader(resource)).getAsJsonObject(); lang = new JsonParser().parse(new InputStreamReader(resource)).getAsJsonObject();
} catch (Exception e) { } catch (Exception e) {
Log.crit(e); Log.crit(e);
@@ -141,18 +147,28 @@ public class Utils {
ArrayList<String> placeholders = new ArrayList<>(); ArrayList<String> placeholders = new ArrayList<>();
if (message.args().size() > 0) { if (message.arguments().size() > 0) {
for (Component component : message.args()) { for (TranslationArgument arg : message.arguments()) {
Component component = arg.asComponent();
if (component instanceof TranslatableComponent) { if (component instanceof TranslatableComponent) {
placeholders.add(translate((TranslatableComponent) component)); placeholders.add(translate((TranslatableComponent) component));
} else if (component instanceof TextComponent) { } else if (arg instanceof TextComponent) {
placeholders.add(getFullText((TextComponent) component, false)); placeholders.add(getFullText((TextComponent) component, false));
} }
} }
//Log.info(Arrays.toString(placeholders.toArray())); //Log.info(Arrays.toString(placeholders.toArray()));
try {
return String.format(base, placeholders.toArray()); return String.format(base, placeholders.toArray());
} catch (Exception e) {
Log.error("Error formatting '"+base+"' with placeholders " + Arrays.toString(placeholders.toArray()));
}
} else { } else {
return base; return base;
} }
} catch (Exception e) {
Log.error(e);
}
return "";
} }
} }
File diff suppressed because it is too large. Load diff