diff --git a/Emulator/src/main/java/com/eu/habbo/Emulator.java b/Emulator/src/main/java/com/eu/habbo/Emulator.java index de1a4e86..720b6af0 100644 --- a/Emulator/src/main/java/com/eu/habbo/Emulator.java +++ b/Emulator/src/main/java/com/eu/habbo/Emulator.java @@ -18,6 +18,8 @@ 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.fusesource.jansi.AnsiConsole; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +40,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. @@ -65,7 +73,6 @@ public final class Emulator { "██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝███████║ ██║ ██║ ██║██║ ██║\n" + "╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\n" + "Still Rocking in 2026.\n"; - public static String build = ""; public static long buildTimestamp = -1L; @@ -104,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(); @@ -119,7 +124,7 @@ public final class Emulator { ConsoleCommand.load(); Emulator.logging = new Logging(); - System.out.println(logo); + System.out.println(startupHero(styledConsole)); long startTime = System.nanoTime(); @@ -153,14 +158,10 @@ 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", "0"); + Emulator.config.register("gui.autostart.enabled", "0"); 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*"); @@ -198,7 +199,7 @@ public final class Emulator { Emulator.isReady = true; Emulator.timeStarted = getIntUnixTimestamp(); - if (Emulator.getConfig().getBoolean("gui.enabled", true)) { + if (shouldLaunchGui()) { EmulatorDashboard.launch(); } @@ -310,6 +311,97 @@ 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"; + } + + 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" + + " | |\\/| | | | | |_) | \\| || || \\| | | _\\___ \\ | | / _ \\ | |_) |\n" + + " | | | | |_| | _ <| |\\ || || |\\ | |_| |___) || |/ ___ \\| _ < \n" + + " |_| |_|\\___/|_| \\_\\_| \\_|___|_| \\_|\\____|____/ |_/_/ \\_\\_| \\_\\\n" + + "\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"; + } + + static boolean shouldStyleConsole(Map environment, boolean interactiveConsole, String osName, String styleProperty) { + 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); + } + } + + 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) { + 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/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..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 @@ -90,7 +90,13 @@ 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())); + 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(), @@ -101,6 +107,11 @@ 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)", total); + } + public boolean canUse(Habbo habbo, Category category, int id) { if (id == 0) return true; if (!this.enforce) return true; 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/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 f4a62d3f..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} [%-14thread] %-5level %-36logger{36} - %msg%n + %d{HH:mm:ss.SSS} %morningstarLevel [%-12thread] %morningstarLogger | %msg%n @@ -65,4 +68,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..4e0d3724 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/ConsoleLogbackLayoutTest.java @@ -0,0 +1,21 @@ +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("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/EmulatorStartupConsoleTest.java b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java new file mode 100644 index 00000000..c748f081 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/EmulatorStartupConsoleTest.java @@ -0,0 +1,88 @@ +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; + +class EmulatorStartupConsoleTest { + @Test + void startupHeroUsesUniversalAsciiLayout() { + String hero = Emulator.startupHero(); + + assertTrue(hero.contains("__ __ ___ ____")); + assertTrue(hero.contains("MORNINGSTAR EXTENDED")); + assertTrue(hero.contains("Version")); + 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")); + } + + @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"); + } + + @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\", \"0\")"), + "gui.enabled must be registered disabled by default so it does not log missing config errors or start the UI unexpectedly"); + 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"); + } +} 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..531ed49e --- /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)", + InfostandBackgroundManager.summary(188, 22, 9, 16, 25)); + } +} 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")); + } +}