From ede7eb8284b71bf9eda986dec61cc04c0f149be6 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:19:17 +0200 Subject: [PATCH 01/10] style(startup): tidy console banner logs Shorten the infostand background startup message into a compact asset summary and print the project/version/build details as a single ASCII startup card instead of several timestamped log lines. Add a small contract test for the compact infostand summary format. --- .../src/main/java/com/eu/habbo/Emulator.java | 20 ++++++++++++------- .../infostand/InfostandBackgroundManager.java | 10 ++++++++-- .../InfostandBackgroundManagerTest.java | 14 +++++++++++++ 3 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index de1a4e86..b6ee12a3 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -154,13 +154,7 @@ public final class Emulator { Emulator.config.register("camera.render.delay", "5"); Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId()); String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId()); - System.out.println(); - LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, "); - System.out.println(); - LOGGER.info("This project is for educational purposes only. This Emulator is an open-source fork of Arcturus created by TheGeneral."); - LOGGER.info("Version: {}", version); - LOGGER.info("Build: {}", build); - LOGGER.info("Build Timestamp: {} [{}]", formatBuildTimestamp(buildTimestamp, hotelTimezoneId), hotelTimezoneId); + System.out.println(startupCard(hotelTimezoneId)); Emulator.texts.register("camera.permission", "You don't have permission to use the camera!"); Emulator.texts.register("camera.wait", "Please wait %seconds% seconds before making another picture."); Emulator.texts.register("camera.error.creation", "Failed to create your picture. *sadpanda*"); @@ -310,6 +304,18 @@ public final class Emulator { return -1L; } + static String startupCard(String hotelTimezoneId) { + return "\n" + + "+----------------------------------------------------------------+\n" + + "| Arcturus Morningstar Extended |\n" + + "| Source : github.com/duckietm/Arcturus-Morningstar-Extended |\n" + + "| Scope : Educational open-source fork by TheGeneral |\n" + + "| Version: " + version + "\n" + + "| Build : " + build + "\n" + + "| Time : " + formatBuildTimestamp(buildTimestamp, hotelTimezoneId) + " [" + hotelTimezoneId + "]\n" + + "+----------------------------------------------------------------+\n"; + } + private static String formatBuildTimestamp(long buildTimestamp, String timezoneId) { if (buildTimestamp <= 0) { return "UNKNOWN"; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java index 8217bbfe..125635a9 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java @@ -90,17 +90,23 @@ public class InfostandBackgroundManager { this.enforce = loaded > 0; if (this.enforce) { - LOGGER.info("InfostandBackgroundManager -> Loaded {} backgrounds, {} stands, {} overlays, {} cards, {} borders from infostand_backgrounds.", + LOGGER.info(summary( this.entries.get(Category.BACKGROUND).size(), this.entries.get(Category.STAND).size(), this.entries.get(Category.OVERLAY).size(), this.entries.get(Category.CARD).size(), - this.entries.get(Category.BORDER).size()); + this.entries.get(Category.BORDER).size())); } else { LOGGER.info("InfostandBackgroundManager -> infostand_backgrounds is empty, server-side validation disabled (only range clamp will apply)."); } } + static String summary(int backgrounds, int stands, int overlays, int cards, int borders) { + int total = backgrounds + stands + overlays + cards + borders; + return String.format("Infostand Background Manager -> Loaded! (%d assets: %d bg, %d stands, %d overlays, %d cards, %d borders)", + total, backgrounds, stands, overlays, cards, borders); + } + public boolean canUse(Habbo habbo, Category category, int id) { if (id == 0) return true; if (!this.enforce) return true; diff --git a/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java b/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java new file mode 100644 index 00000000..93db4bf1 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java @@ -0,0 +1,14 @@ +package com.eu.habbo.habbohotel.users.infostand; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class InfostandBackgroundManagerTest { + @Test + void summaryKeepsStartupLogCompact() { + assertEquals( + "Infostand Background Manager -> Loaded! (260 assets: 188 bg, 22 stands, 9 overlays, 16 cards, 25 borders)", + InfostandBackgroundManager.summary(188, 22, 9, 16, 25)); + } +} From 16d89cdb31534f50a893bd67f847a4dfd5890170 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:29:53 +0200 Subject: [PATCH 02/10] style(startup): customize emulator console banner Add a clean ASCII startup banner for the emulator CMD window and use it instead of the legacy wide block logo. The new banner stays ASCII-only for Windows console compatibility and keeps the Morningstar identity visible before the startup logs. --- Emulator/src/main/java/com/eu/habbo/Emulator.java | 12 +++++++++++- 1 file changed, 11 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 b6ee12a3..87d41a06 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -65,6 +65,16 @@ public final class Emulator { "██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝███████║ ██║ ██║ ██║██║ ██║\n" + "╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\n" + "Still Rocking in 2026.\n"; + private static final String consoleLogo = + "\n" + + "============================================================\n" + + " __ __ ___ ____ _ _ ___ _ _ ____ ____ _____ _ ____ \n" + + " | \\/ |/ _ \\| _ \\| \\ | |_ _| \\ | |/ ___/ ___|_ _|/ \\ | _ \\ \n" + + " | |\\/| | | | | |_) | \\| || || \\| | | _\\___ \\ | | / _ \\ | |_) |\n" + + " | | | | |_| | _ <| |\\ || || |\\ | |_| |___) || |/ ___ \\| _ < \n" + + " |_| |_|\\___/|_| \\_\\_| \\_|___|_| \\_|\\____|____/ |_/_/ \\_\\_| \\_\\\n" + + " Arcturus Morningstar Extended - 2026\n" + + "============================================================\n"; public static String build = ""; public static long buildTimestamp = -1L; @@ -119,7 +129,7 @@ public final class Emulator { ConsoleCommand.load(); Emulator.logging = new Logging(); - System.out.println(logo); + System.out.println(consoleLogo); long startTime = System.nanoTime(); From ea552589792d31d48393e74f073f660877bc1a39 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:34:29 +0200 Subject: [PATCH 03/10] style(startup): use universal console splash Replace the temporary ASCII-art banner with a structured startup splash that uses plain ASCII, aligned fields, and no ANSI or terminal-specific features. This keeps the emulator startup readable across CMD, PowerShell, Linux terminals, Docker logs, CI output, and copied log files. Add a contract test to keep the splash universal. --- .../src/main/java/com/eu/habbo/Emulator.java | 34 ++++++++++++------- .../eu/habbo/EmulatorStartupConsoleTest.java | 18 ++++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 87d41a06..38fb74e9 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -65,17 +65,6 @@ public final class Emulator { "██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝███████║ ██║ ██║ ██║██║ ██║\n" + "╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\n" + "Still Rocking in 2026.\n"; - private static final String consoleLogo = - "\n" + - "============================================================\n" + - " __ __ ___ ____ _ _ ___ _ _ ____ ____ _____ _ ____ \n" + - " | \\/ |/ _ \\| _ \\| \\ | |_ _| \\ | |/ ___/ ___|_ _|/ \\ | _ \\ \n" + - " | |\\/| | | | | |_) | \\| || || \\| | | _\\___ \\ | | / _ \\ | |_) |\n" + - " | | | | |_| | _ <| |\\ || || |\\ | |_| |___) || |/ ___ \\| _ < \n" + - " |_| |_|\\___/|_| \\_\\_| \\_|___|_| \\_|\\____|____/ |_/_/ \\_\\_| \\_\\\n" + - " Arcturus Morningstar Extended - 2026\n" + - "============================================================\n"; - public static String build = ""; public static long buildTimestamp = -1L; @@ -129,7 +118,7 @@ public final class Emulator { ConsoleCommand.load(); Emulator.logging = new Logging(); - System.out.println(consoleLogo); + System.out.println(startupHero()); long startTime = System.nanoTime(); @@ -326,6 +315,27 @@ public final class Emulator { "+----------------------------------------------------------------+\n"; } + static String startupHero() { + return "\n" + + "+------------------------------------------------------------------------------+\n" + + "| MORNINGSTAR EXTENDED |\n" + + "| Arcturus game server runtime |\n" + + "+------------------------------------------------------------------------------+\n" + + "| Version : " + fit(version, 63) + " |\n" + + "| Build : " + fit(build.isBlank() ? "UNKNOWN" : build, 63) + " |\n" + + "| Runtime : " + fit("Java " + System.getProperty("java.version", "unknown") + " / universal console output", 63) + " |\n" + + "+------------------------------------------------------------------------------+\n"; + } + + private static String fit(String value, int width) { + String safe = value == null ? "" : value; + if (safe.length() > width) { + return safe.substring(0, Math.max(0, width - 3)) + "..."; + } + + return String.format("%-" + width + "s", safe); + } + private static String formatBuildTimestamp(long buildTimestamp, String timezoneId) { if (buildTimestamp <= 0) { return "UNKNOWN"; diff --git a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java new file mode 100644 index 00000000..629c2390 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -0,0 +1,18 @@ +package com.eu.habbo; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EmulatorStartupConsoleTest { + @Test + void startupHeroUsesUniversalAsciiLayout() { + String hero = Emulator.startupHero(); + + assertTrue(hero.contains("MORNINGSTAR EXTENDED")); + assertTrue(hero.contains("Version")); + assertTrue(hero.contains("Build")); + assertFalse(hero.contains("\u001B["), "startup hero must not require ANSI support"); + } +} From 9edb984f56764bd17da8a7e99d6df51e62bea2b3 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:43:40 +0200 Subject: [PATCH 04/10] style(startup): improve universal console layout Keep the Morningstar ASCII logo while using a structured plain-text startup card that works in CMD, Windows Terminal, and other consoles without ANSI support. Compact the Logback console pattern to use simple class names, clean separators, and a wider message column so startup logs do not wrap as aggressively. Simplify Infostand startup output to a one-line asset count while preserving category breakdown at DEBUG level. Also normalize generic server start/stop messages so Game Server and RCON Server are labeled correctly instead of being glued to host:port output. --- .../src/main/java/com/eu/habbo/Emulator.java | 6 ++++++ .../infostand/InfostandBackgroundManager.java | 9 +++++++-- .../java/com/eu/habbo/networking/Server.java | 6 +++--- Emulator/src/main/resources/logback.xml | 4 ++-- .../eu/habbo/ConsoleLogbackLayoutTest.java | 20 +++++++++++++++++++ .../eu/habbo/EmulatorStartupConsoleTest.java | 1 + .../InfostandBackgroundManagerTest.java | 2 +- 7 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 38fb74e9..fe88ec9a 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -317,6 +317,12 @@ public final class Emulator { static String startupHero() { return "\n" + + " __ __ ___ ____ _ _ ___ _ _ ____ ____ _____ _ ____ \n" + + " | \\/ |/ _ \\| _ \\| \\ | |_ _| \\ | |/ ___/ ___|_ _|/ \\ | _ \\ \n" + + " | |\\/| | | | | |_) | \\| || || \\| | | _\\___ \\ | | / _ \\ | |_) |\n" + + " | | | | |_| | _ <| |\\ || || |\\ | |_| |___) || |/ ___ \\| _ < \n" + + " |_| |_|\\___/|_| \\_\\_| \\_|___|_| \\_|\\____|____/ |_/_/ \\_\\_| \\_\\\n" + + "\n" + "+------------------------------------------------------------------------------+\n" + "| MORNINGSTAR EXTENDED |\n" + "| Arcturus game server runtime |\n" + diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java index 125635a9..3e2805c8 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java @@ -96,6 +96,12 @@ public class InfostandBackgroundManager { this.entries.get(Category.OVERLAY).size(), this.entries.get(Category.CARD).size(), this.entries.get(Category.BORDER).size())); + LOGGER.debug("Infostand Background Manager assets: {} bg, {} stands, {} overlays, {} cards, {} borders", + this.entries.get(Category.BACKGROUND).size(), + this.entries.get(Category.STAND).size(), + this.entries.get(Category.OVERLAY).size(), + this.entries.get(Category.CARD).size(), + this.entries.get(Category.BORDER).size()); } else { LOGGER.info("InfostandBackgroundManager -> infostand_backgrounds is empty, server-side validation disabled (only range clamp will apply)."); } @@ -103,8 +109,7 @@ public class InfostandBackgroundManager { static String summary(int backgrounds, int stands, int overlays, int cards, int borders) { int total = backgrounds + stands + overlays + cards + borders; - return String.format("Infostand Background Manager -> Loaded! (%d assets: %d bg, %d stands, %d overlays, %d cards, %d borders)", - total, backgrounds, stands, overlays, cards, borders); + return String.format("Infostand Background Manager -> Loaded! (%d assets)", total); } public boolean canUse(Habbo habbo, Category category, int id) { diff --git a/Emulator/src/main/java/com/eu/habbo/networking/Server.java b/Emulator/src/main/java/com/eu/habbo/networking/Server.java index 61258f2d..ec24c314 100644 --- a/Emulator/src/main/java/com/eu/habbo/networking/Server.java +++ b/Emulator/src/main/java/com/eu/habbo/networking/Server.java @@ -85,10 +85,10 @@ public abstract class Server { } if (!channelFuture.isSuccess()) { - LOGGER.info("Failed to connect to the host ({}:{})@{}", this.host, this.port, this.name); + LOGGER.info("Failed to start {} on {}:{}", this.name, this.host, this.port); System.exit(0); } else { - LOGGER.info("Started GameServer on {}:{}@{}", this.host, this.port, this.name); + LOGGER.info("Started {} on {}:{}", this.name, this.host, this.port); } } @@ -100,7 +100,7 @@ public abstract class Server { } catch(InterruptedException e) { LOGGER.error("Exception during {} shutdown... HARD STOP", this.name, e); } - LOGGER.info("GameServer Stopped!"); + LOGGER.info("Stopped {}", this.name); } public ServerBootstrap getServerBootstrap() { diff --git a/Emulator/src/main/resources/logback.xml b/Emulator/src/main/resources/logback.xml index f4a62d3f..5a972548 100644 --- a/Emulator/src/main/resources/logback.xml +++ b/Emulator/src/main/resources/logback.xml @@ -2,7 +2,7 @@ - %d{HH:mm:ss.SSS} [%-14thread] %-5level %-36logger{36} - %msg%n + %d{HH:mm:ss.SSS} %-5level [%-12thread] %-22logger{0} | %msg%n @@ -65,4 +65,4 @@ - \ No newline at end of file + diff --git a/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java b/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java new file mode 100644 index 00000000..0f044868 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java @@ -0,0 +1,20 @@ +package com.eu.habbo; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConsoleLogbackLayoutTest { + @Test + void consolePatternKeepsStartupMessagesReadable() throws Exception { + String logback = Files.readString(Path.of("src/main/resources/logback.xml")); + + assertTrue(logback.contains("%-22logger{0}"), "console should show compact class names"); + assertTrue(logback.contains("| %msg%n"), "console should leave a clear message column"); + assertFalse(logback.contains("%-36logger{36}"), "wide package loggers waste console space"); + } +} diff --git a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java index 629c2390..4e86f21e 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -10,6 +10,7 @@ class EmulatorStartupConsoleTest { void startupHeroUsesUniversalAsciiLayout() { String hero = Emulator.startupHero(); + assertTrue(hero.contains("__ __ ___ ____")); assertTrue(hero.contains("MORNINGSTAR EXTENDED")); assertTrue(hero.contains("Version")); assertTrue(hero.contains("Build")); diff --git a/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java b/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java index 93db4bf1..531ed49e 100644 --- a/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java +++ b/Emulator/src/test/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManagerTest.java @@ -8,7 +8,7 @@ class InfostandBackgroundManagerTest { @Test void summaryKeepsStartupLogCompact() { assertEquals( - "Infostand Background Manager -> Loaded! (260 assets: 188 bg, 22 stands, 9 overlays, 16 cards, 25 borders)", + "Infostand Background Manager -> Loaded! (260 assets)", InfostandBackgroundManager.summary(188, 22, 9, 16, 25)); } } From 98e366dd0752fc3172876a09ee805fb168809dcb Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 16:50:17 +0200 Subject: [PATCH 05/10] 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")); + } } From a8e0534634548b5563f44f636f2448f321608ac0 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 17:01:08 +0200 Subject: [PATCH 06/10] style(logging): colorize adaptive console logs Route console log level and logger columns through custom Logback converters so terminals with ANSI support get colored severity badges and compact colored class names. Keep the same habbo.console.style auto/ansi/plain behavior as the startup splash, including plain fallback for non-interactive output, NO_COLOR, and legacy Windows console paths. The file appenders keep their existing verbose patterns unchanged, so debug/error log files remain plain and grep-friendly. Cover the level formatter, logger formatter, override behavior, and Logback pattern wiring with tests. --- .../src/main/java/com/eu/habbo/Emulator.java | 30 +---- .../util/logback/ConsoleLevelConverter.java | 11 ++ .../util/logback/ConsoleLoggerConverter.java | 11 ++ .../eu/habbo/util/logback/ConsoleStyle.java | 106 ++++++++++++++++++ Emulator/src/main/resources/logback.xml | 5 +- .../eu/habbo/ConsoleLogbackLayoutTest.java | 3 +- .../habbo/util/logback/ConsoleStyleTest.java | 49 ++++++++ 7 files changed, 185 insertions(+), 30 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLevelConverter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLoggerConverter.java create mode 100644 Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleStyle.java create mode 100644 Emulator/src/test/java/com/eu/habbo/util/logback/ConsoleStyleTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 765f676a..26f113ce 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -18,6 +18,7 @@ import com.eu.habbo.plugin.events.emulator.EmulatorStartShutdownEvent; import com.eu.habbo.plugin.events.emulator.EmulatorStoppedEvent; import com.eu.habbo.threading.ThreadPooling; import com.eu.habbo.util.imager.badges.BadgeImager; +import com.eu.habbo.util.logback.ConsoleStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -368,34 +369,7 @@ public final class Emulator { } 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"); + return ConsoleStyle.isEnabled(environment, interactiveConsole, osName, styleProperty); } private static String fit(String value, int width) { diff --git a/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLevelConverter.java b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLevelConverter.java new file mode 100644 index 00000000..43fa820d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLevelConverter.java @@ -0,0 +1,11 @@ +package com.eu.habbo.util.logback; + +import ch.qos.logback.classic.pattern.ClassicConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class ConsoleLevelConverter extends ClassicConverter { + @Override + public String convert(ILoggingEvent event) { + return ConsoleStyle.level(event == null ? null : event.getLevel(), ConsoleStyle.isRuntimeEnabled()); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLoggerConverter.java b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLoggerConverter.java new file mode 100644 index 00000000..61b997d8 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleLoggerConverter.java @@ -0,0 +1,11 @@ +package com.eu.habbo.util.logback; + +import ch.qos.logback.classic.pattern.ClassicConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class ConsoleLoggerConverter extends ClassicConverter { + @Override + public String convert(ILoggingEvent event) { + return ConsoleStyle.logger(event == null ? "" : event.getLoggerName(), ConsoleStyle.isRuntimeEnabled()); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleStyle.java b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleStyle.java new file mode 100644 index 00000000..7c59c2aa --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/util/logback/ConsoleStyle.java @@ -0,0 +1,106 @@ +package com.eu.habbo.util.logback; + +import ch.qos.logback.classic.Level; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +public final class ConsoleStyle { + private static final String ANSI_RESET = "\u001B[0m"; + private static final String ANSI_BOLD = "\u001B[1m"; + private static final String ANSI_DIM = "\u001B[2m"; + 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_RED = "\u001B[31m"; + + private static final int LOGGER_WIDTH = 22; + + private ConsoleStyle() { + } + + public static boolean isRuntimeEnabled() { + return isEnabled( + System.getenv(), + System.console() != null, + System.getProperty("os.name", "Unknown"), + System.getProperty("habbo.console.style", "auto")); + } + + public static boolean isEnabled(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"); + } + + public static String level(Level level, boolean styled) { + String name = level == null ? "INFO" : level.toString(); + String plain = String.format("%-5s", name); + + if (!styled) { + return plain; + } + + if (Level.ERROR.equals(level)) { + return ANSI_BOLD + ANSI_RED + "[x] " + plain + ANSI_RESET; + } + if (Level.WARN.equals(level)) { + return ANSI_YELLOW + "[!] " + plain + ANSI_RESET; + } + if (Level.DEBUG.equals(level) || Level.TRACE.equals(level)) { + return ANSI_DIM + "[.] " + plain + ANSI_RESET; + } + + return ANSI_GREEN + "[i] " + plain + ANSI_RESET; + } + + public static String logger(String loggerName, boolean styled) { + String compact = compactLoggerName(loggerName); + String plain = fit(compact, LOGGER_WIDTH); + return styled ? ANSI_CYAN + plain + ANSI_RESET : plain; + } + + private static String compactLoggerName(String loggerName) { + if (loggerName == null || loggerName.isBlank()) { + return ""; + } + + int lastDot = loggerName.lastIndexOf('.'); + return lastDot >= 0 ? loggerName.substring(lastDot + 1) : loggerName; + } + + private static String fit(String value, int width) { + String safe = value == null ? "" : value; + if (safe.length() > width) { + return safe.substring(0, Math.max(0, width - 3)) + "..."; + } + + return String.format("%-" + width + "s", safe); + } +} diff --git a/Emulator/src/main/resources/logback.xml b/Emulator/src/main/resources/logback.xml index 5a972548..5235b60a 100644 --- a/Emulator/src/main/resources/logback.xml +++ b/Emulator/src/main/resources/logback.xml @@ -1,8 +1,11 @@ + + + - %d{HH:mm:ss.SSS} %-5level [%-12thread] %-22logger{0} | %msg%n + %d{HH:mm:ss.SSS} %morningstarLevel [%-12thread] %morningstarLogger | %msg%n diff --git a/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java b/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java index 0f044868..4e0d3724 100644 --- a/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java +++ b/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java @@ -13,7 +13,8 @@ class ConsoleLogbackLayoutTest { void consolePatternKeepsStartupMessagesReadable() throws Exception { String logback = Files.readString(Path.of("src/main/resources/logback.xml")); - assertTrue(logback.contains("%-22logger{0}"), "console should show compact class names"); + assertTrue(logback.contains("morningstarLevel"), "console should use the adaptive level formatter"); + assertTrue(logback.contains("morningstarLogger"), "console should use the adaptive logger formatter"); assertTrue(logback.contains("| %msg%n"), "console should leave a clear message column"); assertFalse(logback.contains("%-36logger{36}"), "wide package loggers waste console space"); } diff --git a/Emulator/src/test/java/com/eu/habbo/util/logback/ConsoleStyleTest.java b/Emulator/src/test/java/com/eu/habbo/util/logback/ConsoleStyleTest.java new file mode 100644 index 00000000..c62a4e3b --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/util/logback/ConsoleStyleTest.java @@ -0,0 +1,49 @@ +package com.eu.habbo.util.logback; + +import ch.qos.logback.classic.Level; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConsoleStyleTest { + @Test + void formatsLevelWithIconAndColorWhenStyled() { + String formatted = ConsoleStyle.level(Level.WARN, true); + + assertTrue(formatted.contains("\u001B[")); + assertTrue(formatted.contains("[!] WARN ")); + assertTrue(formatted.endsWith("\u001B[0m")); + } + + @Test + void formatsLevelAsPlainTextWhenNotStyled() { + assertEquals("WARN ", ConsoleStyle.level(Level.WARN, false)); + } + + @Test + void formatsLoggerWithColorWhenStyled() { + String formatted = ConsoleStyle.logger("com.eu.habbo.networking.Server", true); + + assertTrue(formatted.contains("\u001B[")); + assertTrue(formatted.contains("Server")); + assertTrue(formatted.endsWith("\u001B[0m")); + } + + @Test + void keepsLoggerPlainAndCompactWhenNotStyled() { + assertEquals("Server ", ConsoleStyle.logger("com.eu.habbo.networking.Server", false)); + } + + @Test + void honorsPlainOverrideEvenInWindowsTerminal() { + assertFalse(ConsoleStyle.isEnabled( + Map.of("WT_SESSION", "abc123"), + true, + "Windows 11", + "plain")); + } +} From 14a590235cbfcc0b707be7fe43570d457915d9ca Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 14 Jun 2026 18:08:50 +0200 Subject: [PATCH 07/10] fix(console): install jansi for forced ansi startup --- .../src/main/java/com/eu/habbo/Emulator.java | 41 +++++++++++++------ .../eu/habbo/EmulatorStartupConsoleTest.java | 14 +++++++ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 26f113ce..caa28594 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -19,6 +19,7 @@ import com.eu.habbo.plugin.events.emulator.EmulatorStoppedEvent; import com.eu.habbo.threading.ThreadPooling; import com.eu.habbo.util.imager.badges.BadgeImager; import com.eu.habbo.util.logback.ConsoleStyle; +import org.fusesource.jansi.AnsiConsole; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,14 +111,12 @@ public final class Emulator { public static void main(String[] args) throws Exception { try { - if (OS_NAME.startsWith("Windows") && !CLASS_PATH.contains("idea_rt.jar")) { - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - ConsoleAppender appender = (ConsoleAppender) root.getAppender("Console"); - - appender.stop(); - appender.setWithJansi(true); - appender.start(); - } + boolean styledConsole = shouldStyleConsole( + System.getenv(), + System.console() != null, + OS_NAME, + System.getProperty("habbo.console.style", "auto")); + configureAnsiConsole(styledConsole); Locale.setDefault(Locale.of("en")); setBuild(); @@ -125,11 +124,7 @@ public final class Emulator { ConsoleCommand.load(); Emulator.logging = new Logging(); - System.out.println(startupHero(shouldStyleConsole( - System.getenv(), - System.console() != null, - OS_NAME, - System.getProperty("habbo.console.style", "auto")))); + System.out.println(startupHero(styledConsole)); long startTime = System.nanoTime(); @@ -372,6 +367,26 @@ public final class Emulator { return ConsoleStyle.isEnabled(environment, interactiveConsole, osName, styleProperty); } + static void configureAnsiConsole(boolean styledConsole) { + if (!styledConsole || !OS_NAME.startsWith("Windows") || CLASS_PATH.contains("idea_rt.jar")) { + return; + } + + try { + AnsiConsole.systemInstall(); + + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + ConsoleAppender appender = (ConsoleAppender) root.getAppender("Console"); + if (appender != null) { + appender.stop(); + appender.setWithJansi(true); + appender.start(); + } + } catch (Throwable e) { + LOGGER.debug("Unable to install Jansi console bridge; continuing with raw console output.", e); + } + } + 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 3f61b09b..aa496cbe 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -3,6 +3,8 @@ package com.eu.habbo; import org.junit.jupiter.api.Test; import java.util.Map; +import java.nio.file.Files; +import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -55,4 +57,16 @@ class EmulatorStartupConsoleTest { "Windows 11", "plain")); } + + @Test + void windowsAnsiModeInstallsJansiBeforePrintingStartupHero() throws Exception { + String source = Files.readString(Path.of("src/main/java/com/eu/habbo/Emulator.java")); + + assertTrue(source.contains("AnsiConsole.systemInstall()"), + "forced ANSI mode must install the Jansi bridge for Windows CMD/System.out"); + assertTrue(source.contains("configureAnsiConsole(styledConsole)"), + "console bridge must be configured before startupHero is printed"); + assertTrue(source.indexOf("configureAnsiConsole(styledConsole)") < source.indexOf("startupHero(styledConsole)"), + "Jansi must be installed before writing ANSI startup output"); + } } From 61972dafa43a0eb2e5fa7efaafa6b3fa7a2fba74 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 14 Jun 2026 18:12:55 +0200 Subject: [PATCH 08/10] fix(config): register gui enabled default --- Emulator/src/main/java/com/eu/habbo/Emulator.java | 1 + .../java/com/eu/habbo/EmulatorStartupConsoleTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index caa28594..ecae8893 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -158,6 +158,7 @@ public final class Emulator { Emulator.config.register("camera.price.points.type", "5"); Emulator.config.register("camera.render.delay", "5"); Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId()); + Emulator.config.register("gui.enabled", "1"); String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId()); System.out.println(startupCard(hotelTimezoneId)); Emulator.texts.register("camera.permission", "You don't have permission to use the camera!"); diff --git a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java index aa496cbe..5ba1bc83 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -69,4 +69,14 @@ class EmulatorStartupConsoleTest { assertTrue(source.indexOf("configureAnsiConsole(styledConsole)") < source.indexOf("startupHero(styledConsole)"), "Jansi must be installed before writing ANSI startup output"); } + + @Test + void registersGuiEnabledBeforeReadingIt() throws Exception { + String source = Files.readString(Path.of("src/main/java/com/eu/habbo/Emulator.java")); + + assertTrue(source.contains("register(\"gui.enabled\", \"1\")"), + "gui.enabled must be registered so the default GUI toggle does not log missing config errors"); + assertTrue(source.indexOf("register(\"gui.enabled\", \"1\")") < source.indexOf("getBoolean(\"gui.enabled\", true)"), + "gui.enabled must be registered before it is read"); + } } From c6e43c6d55978074a0bb77e201cbe3c1e8accf3a Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 14 Jun 2026 18:18:20 +0200 Subject: [PATCH 09/10] fix(config): keep gui disabled by default --- Emulator/src/main/java/com/eu/habbo/Emulator.java | 4 ++-- .../test/java/com/eu/habbo/EmulatorStartupConsoleTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index ecae8893..8fbc94cd 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -158,7 +158,7 @@ public final class Emulator { Emulator.config.register("camera.price.points.type", "5"); Emulator.config.register("camera.render.delay", "5"); Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId()); - Emulator.config.register("gui.enabled", "1"); + Emulator.config.register("gui.enabled", "0"); String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId()); System.out.println(startupCard(hotelTimezoneId)); Emulator.texts.register("camera.permission", "You don't have permission to use the camera!"); @@ -198,7 +198,7 @@ public final class Emulator { Emulator.isReady = true; Emulator.timeStarted = getIntUnixTimestamp(); - if (Emulator.getConfig().getBoolean("gui.enabled", true)) { + if (Emulator.getConfig().getBoolean("gui.enabled", false)) { EmulatorDashboard.launch(); } diff --git a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java index 5ba1bc83..3a4f6499 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -74,9 +74,9 @@ class EmulatorStartupConsoleTest { void registersGuiEnabledBeforeReadingIt() throws Exception { String source = Files.readString(Path.of("src/main/java/com/eu/habbo/Emulator.java")); - assertTrue(source.contains("register(\"gui.enabled\", \"1\")"), - "gui.enabled must be registered so the default GUI toggle does not log missing config errors"); - assertTrue(source.indexOf("register(\"gui.enabled\", \"1\")") < source.indexOf("getBoolean(\"gui.enabled\", true)"), + assertTrue(source.contains("register(\"gui.enabled\", \"0\")"), + "gui.enabled must be registered disabled by default so it does not log missing config errors or start the UI unexpectedly"); + assertTrue(source.indexOf("register(\"gui.enabled\", \"0\")") < source.indexOf("getBoolean(\"gui.enabled\", false)"), "gui.enabled must be registered before it is read"); } } From 1a03b8f3a9f21c060a158ef280da0aa1ef3247b5 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 14 Jun 2026 19:00:22 +0200 Subject: [PATCH 10/10] fix(gui): require explicit dashboard autostart --- Emulator/src/main/java/com/eu/habbo/Emulator.java | 7 ++++++- .../java/com/eu/habbo/EmulatorStartupConsoleTest.java | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index 8fbc94cd..720b6af0 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -159,6 +159,7 @@ public final class Emulator { Emulator.config.register("camera.render.delay", "5"); Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId()); Emulator.config.register("gui.enabled", "0"); + Emulator.config.register("gui.autostart.enabled", "0"); String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId()); System.out.println(startupCard(hotelTimezoneId)); Emulator.texts.register("camera.permission", "You don't have permission to use the camera!"); @@ -198,7 +199,7 @@ public final class Emulator { Emulator.isReady = true; Emulator.timeStarted = getIntUnixTimestamp(); - if (Emulator.getConfig().getBoolean("gui.enabled", false)) { + if (shouldLaunchGui()) { EmulatorDashboard.launch(); } @@ -388,6 +389,10 @@ public final class Emulator { } } + static boolean shouldLaunchGui() { + return Emulator.getConfig() != null && Emulator.getConfig().getBoolean("gui.autostart.enabled", false); + } + 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 3a4f6499..c748f081 100644 --- a/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -76,7 +76,13 @@ class EmulatorStartupConsoleTest { assertTrue(source.contains("register(\"gui.enabled\", \"0\")"), "gui.enabled must be registered disabled by default so it does not log missing config errors or start the UI unexpectedly"); - assertTrue(source.indexOf("register(\"gui.enabled\", \"0\")") < source.indexOf("getBoolean(\"gui.enabled\", false)"), - "gui.enabled must be registered before it is read"); + assertTrue(source.contains("register(\"gui.autostart.enabled\", \"0\")"), + "GUI autostart must use a new disabled-by-default key so old gui.enabled=1 settings do not launch the current UI"); + assertTrue(source.indexOf("register(\"gui.autostart.enabled\", \"0\")") < source.indexOf("shouldLaunchGui()"), + "GUI autostart must be registered before the launch decision"); + assertFalse(source.contains("getBoolean(\"gui.enabled\", true)"), + "GUI must not use a true fallback"); + assertFalse(source.contains("getBoolean(\"gui.enabled\", false)"), + "legacy gui.enabled must not control startup anymore"); } }