From 98e366dd0752fc3172876a09ee805fb168809dcb Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:50:17 +0200 Subject: [PATCH] style(startup): add adaptive console colors Add an auto-detected styled startup splash for terminals that support ANSI colors, including Windows Terminal, ANSICON, ConEmu ANSI, and common TERM-based consoles. Keep the default and redirected-output path plain text so legacy CMD, logs, and service wrappers remain readable. The style can also be forced with -Dhabbo.console.style=ansi or disabled with -Dhabbo.console.style=plain. Cover the styled splash, Windows Terminal detection, non-interactive fallback, and forced plain mode with startup console tests. --- .../src/main/java/com/eu/habbo/Emulator.java | 67 ++++++++++++++++++- .../eu/habbo/EmulatorStartupConsoleTest.java | 39 +++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index fe88ec9a..765f676a 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -38,6 +38,12 @@ public final class Emulator { private static final Logger LOGGER = LoggerFactory.getLogger(Emulator.class); private static final String OS_NAME = (System.getProperty("os.name") != null ? System.getProperty("os.name") : "Unknown"); private static final String CLASS_PATH = (System.getProperty("java.class.path") != null ? System.getProperty("java.class.path") : "Unknown"); + private static final String ANSI_RESET = "\u001B[0m"; + private static final String ANSI_BOLD = "\u001B[1m"; + private static final String ANSI_CYAN = "\u001B[36m"; + private static final String ANSI_GREEN = "\u001B[32m"; + private static final String ANSI_YELLOW = "\u001B[33m"; + private static final String ANSI_DIM = "\u001B[2m"; // Fallback version, only used when running outside a packaged jar (e.g. from // the IDE). In production the version comes from the jar manifest below. @@ -118,7 +124,11 @@ public final class Emulator { ConsoleCommand.load(); Emulator.logging = new Logging(); - System.out.println(startupHero()); + System.out.println(startupHero(shouldStyleConsole( + System.getenv(), + System.console() != null, + OS_NAME, + System.getProperty("habbo.console.style", "auto")))); long startTime = System.nanoTime(); @@ -316,6 +326,30 @@ public final class Emulator { } static String startupHero() { + return startupHero(false); + } + + static String startupHero(boolean styled) { + if (styled) { + return "\n" + + ANSI_CYAN + + " __ __ ___ ____ _ _ ___ _ _ ____ ____ _____ _ ____ \n" + + " | \\/ |/ _ \\| _ \\| \\ | |_ _| \\ | |/ ___/ ___|_ _|/ \\ | _ \\ \n" + + " | |\\/| | | | | |_) | \\| || || \\| | | _\\___ \\ | | / _ \\ | |_) |\n" + + " | | | | |_| | _ <| |\\ || || |\\ | |_| |___) || |/ ___ \\| _ < \n" + + " |_| |_|\\___/|_| \\_\\_| \\_|___|_| \\_|\\____|____/ |_/_/ \\_\\_| \\_\\\n" + + ANSI_RESET + + "\n" + + ANSI_DIM + "+------------------------------------------------------------------------------+" + ANSI_RESET + "\n" + + "| " + ANSI_BOLD + ANSI_GREEN + "[OK] MORNINGSTAR EXTENDED" + ANSI_RESET + fit("", 50) + " |\n" + + "| " + ANSI_DIM + "Arcturus game server runtime" + ANSI_RESET + fit("", 48) + " |\n" + + ANSI_DIM + "+------------------------------------------------------------------------------+" + ANSI_RESET + "\n" + + "| " + ANSI_YELLOW + "[VER]" + ANSI_RESET + " Version : " + fit(version, 57) + " |\n" + + "| " + ANSI_YELLOW + "[BLD]" + ANSI_RESET + " Build : " + fit(build.isBlank() ? "UNKNOWN" : build, 57) + " |\n" + + "| " + ANSI_YELLOW + "[JVM]" + ANSI_RESET + " Runtime : " + fit("Java " + System.getProperty("java.version", "unknown") + " / styled console output", 57) + " |\n" + + ANSI_DIM + "+------------------------------------------------------------------------------+" + ANSI_RESET + "\n"; + } + return "\n" + " __ __ ___ ____ _ _ ___ _ _ ____ ____ _____ _ ____ \n" + " | \\/ |/ _ \\| _ \\| \\ | |_ _| \\ | |/ ___/ ___|_ _|/ \\ | _ \\ \n" + @@ -333,6 +367,37 @@ public final class Emulator { "+------------------------------------------------------------------------------+\n"; } + static boolean shouldStyleConsole(Map environment, boolean interactiveConsole, String osName, String styleProperty) { + String style = styleProperty == null ? "auto" : styleProperty.trim().toLowerCase(Locale.ROOT); + if (style.equals("ansi") || style.equals("color") || style.equals("colours") || style.equals("colors")) { + return true; + } + if (style.equals("plain") || style.equals("none") || style.equals("false") || style.equals("off")) { + return false; + } + if (!interactiveConsole) { + return false; + } + + Map env = environment == null ? Collections.emptyMap() : environment; + if (env.containsKey("NO_COLOR")) { + return false; + } + if (env.containsKey("WT_SESSION") || env.containsKey("ANSICON") || "ON".equalsIgnoreCase(env.get("ConEmuANSI"))) { + return true; + } + + String term = env.getOrDefault("TERM", ""); + if (term.equalsIgnoreCase("dumb")) { + return false; + } + if (!term.isBlank() && (term.contains("xterm") || term.contains("ansi") || term.contains("screen") || term.contains("tmux"))) { + return true; + } + + return osName == null || !osName.toLowerCase(Locale.ROOT).startsWith("windows"); + } + private static String fit(String value, int width) { String safe = value == null ? "" : value; if (safe.length() > width) { diff --git a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java index 4e86f21e..3f61b09b 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -2,6 +2,8 @@ package com.eu.habbo; import org.junit.jupiter.api.Test; +import java.util.Map; + import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -16,4 +18,41 @@ class EmulatorStartupConsoleTest { assertTrue(hero.contains("Build")); assertFalse(hero.contains("\u001B["), "startup hero must not require ANSI support"); } + + @Test + void startupHeroCanRenderStyledLayoutWhenAnsiIsAvailable() { + String hero = Emulator.startupHero(true); + + assertTrue(hero.contains("\u001B["), "styled hero should include ANSI colors"); + assertTrue(hero.contains("[OK] MORNINGSTAR EXTENDED")); + assertTrue(hero.contains("[JVM]")); + assertTrue(hero.endsWith("\u001B[0m\n"), "styled hero should reset terminal attributes"); + } + + @Test + void consoleStyleAutoDetectsWindowsTerminal() { + assertTrue(Emulator.shouldStyleConsole( + Map.of("WT_SESSION", "abc123"), + true, + "Windows 11", + "auto")); + } + + @Test + void consoleStyleFallsBackWhenOutputIsNotInteractive() { + assertFalse(Emulator.shouldStyleConsole( + Map.of("WT_SESSION", "abc123"), + false, + "Windows 11", + "auto")); + } + + @Test + void consoleStyleCanBeForcedOff() { + assertFalse(Emulator.shouldStyleConsole( + Map.of("WT_SESSION", "abc123"), + true, + "Windows 11", + "plain")); + } }