diff --git a/src/main/java/de/bixilon/minosoft/config/Configuration.java b/src/main/java/de/bixilon/minosoft/config/Configuration.java index c3514089c..9c77870fb 100644 --- a/src/main/java/de/bixilon/minosoft/config/Configuration.java +++ b/src/main/java/de/bixilon/minosoft/config/Configuration.java @@ -214,6 +214,8 @@ public class Configuration { case NETWORK_FAKE_CLIENT_BRAND -> json.getAsJsonObject("network").get("fake-network-brand").getAsBoolean(); case NETWORK_SHOW_LAN_SERVERS -> json.getAsJsonObject("network").get("show-lan-servers").getAsBoolean(); case DEBUG_VERIFY_ASSETS -> json.getAsJsonObject("debug").get("verify-assets").getAsBoolean(); + case CHAT_COLORED -> json.getAsJsonObject("chat").get("colored").getAsBoolean(); + case CHAT_OBFUSCATED -> json.getAsJsonObject("chat").get("obfuscated").getAsBoolean(); }; } if (path instanceof ConfigurationPaths.IntegerPaths integerPath) { @@ -240,6 +242,8 @@ public class Configuration { case NETWORK_FAKE_CLIENT_BRAND -> input.getAsJsonObject("network").addProperty("fake-network-brand", bool); case NETWORK_SHOW_LAN_SERVERS -> input.getAsJsonObject("network").addProperty("show-lan-servers", bool); case DEBUG_VERIFY_ASSETS -> input.getAsJsonObject("debug").addProperty("verify-assets", bool); + case CHAT_COLORED -> input.getAsJsonObject("chat").addProperty("colored", bool); + case CHAT_OBFUSCATED -> input.getAsJsonObject("chat").addProperty("obfuscated", bool); } } else if (data instanceof Integer integer) { switch ((ConfigurationPaths.IntegerPaths) path) { diff --git a/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java b/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java index 211d21233..68cab15a2 100644 --- a/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java +++ b/src/main/java/de/bixilon/minosoft/config/ConfigurationPaths.java @@ -36,7 +36,9 @@ public abstract class ConfigurationPaths { public enum BooleanPaths implements ConfigurationPath { NETWORK_FAKE_CLIENT_BRAND, NETWORK_SHOW_LAN_SERVERS, - DEBUG_VERIFY_ASSETS + DEBUG_VERIFY_ASSETS, + CHAT_COLORED, + CHAT_OBFUSCATED } public enum IntegerPaths implements ConfigurationPath { diff --git a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.java b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.java index 465859a00..0b33b193e 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.java @@ -13,7 +13,10 @@ package de.bixilon.minosoft.data.text; +import de.bixilon.minosoft.Minosoft; +import de.bixilon.minosoft.config.ConfigurationPaths; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; +import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.hash.BetterHashSet; import javafx.animation.Animation; import javafx.animation.KeyFrame; @@ -162,18 +165,30 @@ public class TextComponent extends ChatComponent { @Override public ObservableList getJavaFXText(ObservableList nodes) { Text text = new Text(this.text); - if (this.color == null) { - text.setFill(Color.WHITE); - } else { + text.setFill(Color.WHITE); + if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_COLORED) && this.color != null) { text.setFill(Color.web(this.color.toString())); } this.formatting.forEach((chatFormattingCode -> { if (chatFormattingCode instanceof PreChatFormattingCodes code) { switch (code) { case OBFUSCATED -> { - Timeline flasher = new Timeline(new KeyFrame(Duration.seconds(1), e -> text.setVisible(false)), new KeyFrame(Duration.seconds(2), e -> text.setVisible(true))); - flasher.setCycleCount(Animation.INDEFINITE); - flasher.play(); + // ToDo: potential memory leak: Stop timeline, when TextComponent isn't shown anymore + Timeline obfuscatedTimeline; + if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_OBFUSCATED)) { + obfuscatedTimeline = new Timeline(new KeyFrame(Duration.millis(50), e -> { + char[] chars = text.getText().toCharArray(); + for (int i = 0; i < chars.length; i++) { + chars[i] = Util.getRandomChar(ProtocolDefinition.OBFUSCATED_CHARS); + } + text.setText(new String(chars)); + })); + } else { + obfuscatedTimeline = new Timeline(new KeyFrame(Duration.millis(500), e -> text.setVisible(false)), new KeyFrame(Duration.millis(1000), e -> text.setVisible(true))); + } + obfuscatedTimeline.setCycleCount(Animation.INDEFINITE); + obfuscatedTimeline.play(); + text.getStyleClass().add("obfuscated"); } case BOLD -> text.setStyle("-fx-font-weight: bold;"); case STRIKETHROUGH -> text.setStyle("-fx-strikethrough: true;"); diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java index 49ff514e9..d4ae62504 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java @@ -54,6 +54,9 @@ public final class ProtocolDefinition { public static final int SECTIONS_PER_CHUNK = 16; public static final int BLOCKS_PER_SECTION = SECTION_WIDTH_X * SECTION_HEIGHT_Y * SECTION_WIDTH_X; + + public static final char[] OBFUSCATED_CHARS = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".toCharArray(); + static { // java does (why ever) not allow to directly assign a null InetAddress temp; diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index 96bb469ee..b64979d48 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -37,7 +37,7 @@ import java.util.zip.*; public final class Util { public static final Pattern UUID_FIX = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); // thanks https://www.spigotmc.org/threads/free-code-easily-convert-between-trimmed-and-full-uuids.165615 - public static final String RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + public static final char[] RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); public static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final Random THREAD_LOCAL_RANDOM = ThreadLocalRandom.current(); @@ -264,11 +264,19 @@ public final class Util { public static String generateRandomString(int length) { StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i++) { - sb.append(RANDOM_STRING_CHARS.charAt(THREAD_LOCAL_RANDOM.nextInt(RANDOM_STRING_CHARS.length()))); + sb.append(getRandomChar(RANDOM_STRING_CHARS)); } return sb.toString(); } + public static char getRandomChar(char[] chars) { + return chars[(THREAD_LOCAL_RANDOM.nextInt(chars.length))]; + } + + public static char getRandomChar() { + return (char) THREAD_LOCAL_RANDOM.nextInt(); + } + public static String getStringBetween(String search, String first, String second) { String result = search.substring(search.indexOf(first) + first.length()); return result.substring(0, result.indexOf(second)); diff --git a/src/main/resources/config/config.json b/src/main/resources/config/config.json index d2339c820..b53f171e1 100644 --- a/src/main/resources/config/config.json +++ b/src/main/resources/config/config.json @@ -7,6 +7,10 @@ "game": { "render-distance": 12 }, + "chat": { + "colored": true, + "obfuscated": true + }, "network": { "fake-network-brand": false, "show-lan-servers": true diff --git a/src/main/resources/layout/style.css b/src/main/resources/layout/style.css index 5e1571b28..6afa2e81a 100644 --- a/src/main/resources/layout/style.css +++ b/src/main/resources/layout/style.css @@ -198,3 +198,7 @@ .ping-no-connection { -fx-text-fill: red; } + +.obfuscated { + -fx-font-family: "Hack"; +}