From 58b0a61da654eb0864b66278c790aeda07872e16 Mon Sep 17 00:00:00 2001 From: Joltras Date: Mon, 10 Aug 2026 18:47:30 +0200 Subject: [PATCH 1/4] feat(message): add map announcement method --- .../cygnus/common/Messages.java | 39 ++++++++++++- .../cygnus/common/MessagesTest.java | 55 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java diff --git a/common/src/main/java/net/onelitefeather/cygnus/common/Messages.java b/common/src/main/java/net/onelitefeather/cygnus/common/Messages.java index df1243d0..818bf408 100644 --- a/common/src/main/java/net/onelitefeather/cygnus/common/Messages.java +++ b/common/src/main/java/net/onelitefeather/cygnus/common/Messages.java @@ -10,9 +10,11 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * @author theEvilReaper - * @version 1.1.0 + * @version 1.2.0 * @since 1.0.0 **/ public final class Messages { @@ -34,6 +36,7 @@ public final class Messages { private static final Component SURVIVOR_JOIN_PART_UPPER; private static final Component SURVIVOR_JOIN_LOWER_PART; public static final Component SLENDER_JOIN_PART; + private static final int MAP_ANNOUNCEMENT_MIN_WIDTH = 20; static { int forceStartTime = GameConfig.FORCE_START_TIME - 1; @@ -171,4 +174,38 @@ public static Component getSurvivorJoinMessage(String pageCount) { .append(Component.newline()) .append(SURVIVOR_JOIN_LOWER_PART); } + + /** + * Returns a {@link Component} which contains information about which map is used for the game. + * + * @param mapName of the map + * @param builders of the map + * @return the created {@link Component} reference + */ + @Contract(value = "_, _ -> new", pure = true) + public static Component getMapAnnouncementMessage(String mapName, @Nullable List builders) { + boolean hasBuilders = builders != null && !builders.isEmpty(); + String joinedBuilders = hasBuilders ? String.join(", ", builders) : ""; + + int width = Math.max(MAP_ANNOUNCEMENT_MIN_WIDTH, ("Now playing: " + mapName).length()); + if (hasBuilders) { + width = Math.max(width, ("Built by: " + joinedBuilders).length()); + } + Component separator = Component.text("─".repeat(width), NamedTextColor.DARK_GRAY); + + Component message = Component.newline().append(separator) + .append(Component.newline()) + .append(Component.text("Now playing: ", NamedTextColor.GRAY)) + .append(Component.text(mapName, NamedTextColor.GOLD)) + .append(Component.newline()); + + if (hasBuilders) { + message = message + .append(Component.text("Built by: ", NamedTextColor.GRAY)) + .append(Component.text(joinedBuilders, NamedTextColor.AQUA)) + .append(Component.newline()); + } + + return message.append(separator).append(Component.newline()); + } } diff --git a/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java b/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java new file mode 100644 index 00000000..e6ae758d --- /dev/null +++ b/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java @@ -0,0 +1,55 @@ +package net.onelitefeather.cygnus.common; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MessagesTest { + + @Test + void testMapAnnouncementWithBuilders() { + Component message = Messages.getMapAnnouncementMessage("Granskoga", List.of("Alice", "Bob")); + + assertEquals( + "──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────", + PlainTextComponentSerializer.plainText().serialize(message) + ); + } + + @Test + void testMapAnnouncementWithNullBuilders() { + Component message = Messages.getMapAnnouncementMessage("Granskoga", null); + + assertEquals( + "──────────────────────\nNow playing: Granskoga\n──────────────────────", + PlainTextComponentSerializer.plainText().serialize(message) + ); + } + + @Test + void testMapAnnouncementWithEmptyBuilders() { + Component message = Messages.getMapAnnouncementMessage("Granskoga", List.of()); + + assertEquals( + "──────────────────────\nNow playing: Granskoga\n──────────────────────", + PlainTextComponentSerializer.plainText().serialize(message) + ); + } + + @Test + void testMapAnnouncementWidthScalesWithLongestLine() { + Component message = Messages.getMapAnnouncementMessage( + "Very Long Map Name Example", + List.of("Alice", "Bob", "Charlie", "Dave") + ); + + assertEquals( + "───────────────────────────────────────\nNow playing: Very Long Map Name Example\nBuilt by: Alice, Bob, Charlie, Dave\n───────────────────────────────────────", + PlainTextComponentSerializer.plainText().serialize(message) + ); + } +} From 282f6cc3de2027787ccf3b90e11bed6ee330b7b6 Mon Sep 17 00:00:00 2001 From: Joltras Date: Mon, 10 Aug 2026 18:47:44 +0200 Subject: [PATCH 2/4] chore(game): overhaul game map load event and add test --- .../net/onelitefeather/cygnus/Cygnus.java | 2 + .../listener/map/GameMapLoadedListener.java | 17 ++++ .../map/GameMapLoadedListenerTest.java | 85 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 game/src/main/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListener.java create mode 100644 game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java diff --git a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java index ced70c8a..3dfeb864 100644 --- a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java +++ b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java @@ -6,6 +6,7 @@ import net.onelitefeather.cygnus.event.GameStartEvent; import net.onelitefeather.cygnus.common.page.event.PageSpawnEvent; import net.onelitefeather.cygnus.listener.game.GameStartListener; +import net.onelitefeather.cygnus.listener.map.GameMapLoadedListener; import net.onelitefeather.cygnus.listener.page.PageSpawnListener; import net.onelitefeather.cygnus.listener.view.ViewUpdateListener; import net.onelitefeather.cygnus.listener.page.PageDiscoveryCompleteListener; @@ -144,6 +145,7 @@ private void initListener() { manager.addListener(GameMapLoadedEvent.class, event -> this.pageProvider.loadPageData(event.gameMap().getPageFaces()) ); + manager.addListener(GameMapLoadedEvent.class, new GameMapLoadedListener()); manager.addListener(PlayerSpawnEvent.class, new PlayerSpawnListener(player -> this.mapProvider.teleportToSpawn(player, false), phaseSupplier)); PlayerQuitListener quitListener = new PlayerQuitListener(phaseSupplier, teamService, this.staminaService, this.gameConfig.minPlayers()); manager.addListener(PlayerDisconnectEvent.class, quitListener); diff --git a/game/src/main/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListener.java b/game/src/main/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListener.java new file mode 100644 index 00000000..b33c3082 --- /dev/null +++ b/game/src/main/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListener.java @@ -0,0 +1,17 @@ +package net.onelitefeather.cygnus.listener.map; + +import net.onelitefeather.cygnus.common.Messages; +import net.onelitefeather.cygnus.common.map.GameMap; +import net.onelitefeather.cygnus.map.event.GameMapLoadedEvent; +import net.theevilreaper.aves.util.Broadcaster; + +import java.util.function.Consumer; + +public final class GameMapLoadedListener implements Consumer { + + @Override + public void accept(GameMapLoadedEvent event) { + GameMap gameMap = event.gameMap(); + Broadcaster.broadcast(Messages.getMapAnnouncementMessage(gameMap.name(), gameMap.builders())); + } +} diff --git a/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java b/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java new file mode 100644 index 00000000..5966229d --- /dev/null +++ b/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java @@ -0,0 +1,85 @@ +package net.onelitefeather.cygnus.listener.map; + +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.InstanceContainer; +import net.minestom.server.network.packet.server.play.SystemChatPacket; +import net.minestom.testing.Collector; +import net.minestom.testing.Env; +import net.minestom.testing.TestConnection; +import net.onelitefeather.cygnus.CygnusPlayerTestBase; +import net.onelitefeather.cygnus.common.map.GameMap; +import net.onelitefeather.cygnus.map.event.GameMapLoadedEvent; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class GameMapLoadedListenerTest extends CygnusPlayerTestBase { + + @Test + void testBroadcastsMapAnnouncementToOnlinePlayers(@NotNull Env env) { + Instance instance = env.createFlatInstance(); + InstanceContainer gameInstance = (InstanceContainer) env.createFlatInstance(); + + TestConnection connection = env.createConnection(); + connection.connect(instance); + Collector packets = connection.trackIncoming(SystemChatPacket.class); + + GameMap gameMap = new GameMap( + "Granskoga", + Pos.ZERO, + Pos.ZERO, + Set.of(), + Set.of(), + List.of("Alice", "Bob") + ); + + new GameMapLoadedListener().accept(new GameMapLoadedEvent(gameMap, gameInstance)); + + List received = packets.collect(); + assertEquals(1, received.size(), "Player should have received exactly one map announcement message."); + assertEquals( + "──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────", + PlainTextComponentSerializer.plainText().serialize(received.getFirst().message()) + ); + + env.destroyInstance(instance, true); + env.destroyInstance(gameInstance, true); + } + + @Test + void testOmitsBuilderLineWhenMapHasNoBuilders(@NotNull Env env) { + Instance instance = env.createFlatInstance(); + InstanceContainer gameInstance = (InstanceContainer) env.createFlatInstance(); + + TestConnection connection = env.createConnection(); + connection.connect(instance); + Collector packets = connection.trackIncoming(SystemChatPacket.class); + + GameMap gameMap = new GameMap( + "Granskoga", + Pos.ZERO, + Pos.ZERO, + Set.of(), + Set.of(), + List.of() + ); + + new GameMapLoadedListener().accept(new GameMapLoadedEvent(gameMap, gameInstance)); + + List received = packets.collect(); + assertEquals(1, received.size(), "Player should have received exactly one map announcement message."); + assertEquals( + "──────────────────────\nNow playing: Granskoga\n──────────────────────", + PlainTextComponentSerializer.plainText().serialize(received.getFirst().message()) + ); + + env.destroyInstance(instance, true); + env.destroyInstance(gameInstance, true); + } +} From d26a17fa7edb0e273468c9badb8e08679859b780 Mon Sep 17 00:00:00 2001 From: Joltras Date: Mon, 10 Aug 2026 18:48:21 +0200 Subject: [PATCH 3/4] chore: add package info file --- .../net/onelitefeather/cygnus/listener/map/package-info.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 game/src/main/java/net/onelitefeather/cygnus/listener/map/package-info.java diff --git a/game/src/main/java/net/onelitefeather/cygnus/listener/map/package-info.java b/game/src/main/java/net/onelitefeather/cygnus/listener/map/package-info.java new file mode 100644 index 00000000..df973649 --- /dev/null +++ b/game/src/main/java/net/onelitefeather/cygnus/listener/map/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.onelitefeather.cygnus.listener.map; + +import org.jetbrains.annotations.NotNullByDefault; From a617bddbbd29970c7be8c7a82f2d0d442c458172 Mon Sep 17 00:00:00 2001 From: Joltras Date: Mon, 10 Aug 2026 18:49:54 +0200 Subject: [PATCH 4/4] test(game): update message check tests --- .../net/onelitefeather/cygnus/common/MessagesTest.java | 8 ++++---- .../cygnus/listener/map/GameMapLoadedListenerTest.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java b/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java index e6ae758d..cf4ef377 100644 --- a/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java +++ b/common/src/test/java/net/onelitefeather/cygnus/common/MessagesTest.java @@ -15,7 +15,7 @@ void testMapAnnouncementWithBuilders() { Component message = Messages.getMapAnnouncementMessage("Granskoga", List.of("Alice", "Bob")); assertEquals( - "──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────", + "\n──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────\n", PlainTextComponentSerializer.plainText().serialize(message) ); } @@ -25,7 +25,7 @@ void testMapAnnouncementWithNullBuilders() { Component message = Messages.getMapAnnouncementMessage("Granskoga", null); assertEquals( - "──────────────────────\nNow playing: Granskoga\n──────────────────────", + "\n──────────────────────\nNow playing: Granskoga\n──────────────────────\n", PlainTextComponentSerializer.plainText().serialize(message) ); } @@ -35,7 +35,7 @@ void testMapAnnouncementWithEmptyBuilders() { Component message = Messages.getMapAnnouncementMessage("Granskoga", List.of()); assertEquals( - "──────────────────────\nNow playing: Granskoga\n──────────────────────", + "\n──────────────────────\nNow playing: Granskoga\n──────────────────────\n", PlainTextComponentSerializer.plainText().serialize(message) ); } @@ -48,7 +48,7 @@ void testMapAnnouncementWidthScalesWithLongestLine() { ); assertEquals( - "───────────────────────────────────────\nNow playing: Very Long Map Name Example\nBuilt by: Alice, Bob, Charlie, Dave\n───────────────────────────────────────", + "\n───────────────────────────────────────\nNow playing: Very Long Map Name Example\nBuilt by: Alice, Bob, Charlie, Dave\n───────────────────────────────────────\n", PlainTextComponentSerializer.plainText().serialize(message) ); } diff --git a/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java b/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java index 5966229d..504728d9 100644 --- a/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java +++ b/game/src/test/java/net/onelitefeather/cygnus/listener/map/GameMapLoadedListenerTest.java @@ -44,7 +44,7 @@ void testBroadcastsMapAnnouncementToOnlinePlayers(@NotNull Env env) { List received = packets.collect(); assertEquals(1, received.size(), "Player should have received exactly one map announcement message."); assertEquals( - "──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────", + "\n──────────────────────\nNow playing: Granskoga\nBuilt by: Alice, Bob\n──────────────────────\n", PlainTextComponentSerializer.plainText().serialize(received.getFirst().message()) ); @@ -75,7 +75,7 @@ void testOmitsBuilderLineWhenMapHasNoBuilders(@NotNull Env env) { List received = packets.collect(); assertEquals(1, received.size(), "Player should have received exactly one map announcement message."); assertEquals( - "──────────────────────\nNow playing: Granskoga\n──────────────────────", + "\n──────────────────────\nNow playing: Granskoga\n──────────────────────\n", PlainTextComponentSerializer.plainText().serialize(received.getFirst().message()) );