From 0a3528a5cd17e2b9114f3de8bba258aa5f25536b Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 19 Dec 2024 07:58:36 -0800 Subject: [PATCH 01/36] Version 1.3.1 --- pom.xml | 2 +- src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 12209fa..4e62736 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ -LOCAL - 1.3.0 + 1.3.1 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 42961f2..84e9f21 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: BentoBox-Chat main: world.bentobox.chat.ChatPladdon version: ${project.version}${build.number} -api-version: "1.16" +api-version: "1.21" authors: [tastybento] contributors: ["The BentoBoxWorld Community"] From a1e97840d931312524baff40b967c05a4f06148f Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 12 Apr 2025 13:46:06 +0900 Subject: [PATCH 02/36] Update config.yml to latest game modes. --- src/main/resources/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index aba90fc..c882040 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -6,6 +6,10 @@ team-chat: - AcidIsland - CaveBlock - SkyGrid + - Brix + - Poseidon + - AOneBlock + - Parkour # If players are outside a game world, team chat can still exist for one game mode. List it # here if you want that, e.g., BSkyBlock default-teamchat-gamemode: '' @@ -18,6 +22,10 @@ island-chat: - AcidIsland - CaveBlock - SkyGrid + - Brix + - Poseidon + - AOneBlock + - Parkour # Log island chats to console. log: false chat-listener: From f35d0aaeb7e959d5610c01c6c63da4fac9e93490 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 01:57:54 +0000 Subject: [PATCH 03/36] Initial plan From cf926d2a8179f118d8222721d9eed1c5568b7c4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:01:54 +0000 Subject: [PATCH 04/36] Fix NPE in teamChat when getIsland returns null Add null check for the island returned by getIsland() in the teamChat method to prevent NullPointerException when extra arguments are passed to /is teamchat (e.g. /is teamchat test) and the player's island cannot be found. Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- .../java/world/bentobox/chat/listeners/ChatListener.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 1ee29af..7e3cca0 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -126,7 +126,11 @@ public void islandChat(Island i, Player player, String message) { public void teamChat(World w, final Player player, String message) { // Get island members of member or above - addon.getIslands().getIsland(w, player.getUniqueId()).getMemberSet().stream() + Island island = addon.getIslands().getIsland(w, player.getUniqueId()); + if (island == null) { + return; + } + island.getMemberSet().stream() // Map to users .map(User::getInstance) // Filter for online only From 40a0c149b901e69a7b45db3d3805109de63da905 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:49:45 +0000 Subject: [PATCH 05/36] Initial plan From 1d45ff34853672d2740e818d1ca70c22e1ce5897 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:03:02 +0000 Subject: [PATCH 06/36] Add JUnit 5 test classes and update pom.xml for Paper 1.21.11 Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- pom.xml | 62 +++- .../world/bentobox/chat/ChatPladdonTest.java | 27 ++ .../world/bentobox/chat/CommonTestSetup.java | 208 ++++++++++++ .../world/bentobox/chat/SettingsTest.java | 142 ++++++++ .../java/world/bentobox/chat/WhiteBox.java | 24 ++ .../admin/AdminIslandChatSpyCommandTest.java | 67 ++++ .../admin/AdminTeamChatSpyCommandTest.java | 67 ++++ .../island/IslandChatCommandTest.java | 109 ++++++ .../island/IslandTeamChatCommandTest.java | 105 ++++++ .../chat/listeners/ChatListenerTest.java | 320 ++++++++++++++++++ .../IsTeamChatHandlerTest.java | 97 ++++++ 11 files changed, 1210 insertions(+), 18 deletions(-) create mode 100644 src/test/java/world/bentobox/chat/ChatPladdonTest.java create mode 100644 src/test/java/world/bentobox/chat/CommonTestSetup.java create mode 100644 src/test/java/world/bentobox/chat/SettingsTest.java create mode 100644 src/test/java/world/bentobox/chat/WhiteBox.java create mode 100644 src/test/java/world/bentobox/chat/commands/admin/AdminIslandChatSpyCommandTest.java create mode 100644 src/test/java/world/bentobox/chat/commands/admin/AdminTeamChatSpyCommandTest.java create mode 100644 src/test/java/world/bentobox/chat/commands/island/IslandChatCommandTest.java create mode 100644 src/test/java/world/bentobox/chat/commands/island/IslandTeamChatCommandTest.java create mode 100644 src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java create mode 100644 src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java diff --git a/pom.xml b/pom.xml index 4e62736..6fb1e6b 100644 --- a/pom.xml +++ b/pom.xml @@ -60,10 +60,12 @@ UTF-8 21 - 2.0.9 + 5.10.2 + 5.11.0 + v1.21-SNAPSHOT - 1.21.3-R0.1-SNAPSHOT - 2.7.1-SNAPSHOT + 1.21.11-R0.1-SNAPSHOT + 3.10.2 ${build.version}-SNAPSHOT @@ -115,6 +117,10 @@ + + papermc + https://repo.papermc.io/repository/maven-public/ + spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots @@ -131,33 +137,51 @@ codemc-public https://repo.codemc.org/repository/maven-public/ + + codemc-snapshots + https://repo.codemc.org/repository/maven-snapshots/ + - + - org.spigotmc - spigot-api - ${spigot.version} + io.papermc.paper + paper-api + ${paper.version} provided - + - org.mockito - mockito-core - 3.11.2 + com.github.MockBukkit + MockBukkit + ${mock-bukkit.version} test + - org.powermock - powermock-module-junit4 - ${powermock.version} + org.junit.jupiter + junit-jupiter-api + ${junit.version} test - org.powermock - powermock-api-mockito2 - ${powermock.version} + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} test @@ -215,7 +239,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.5.2 ${argLine} @@ -246,6 +270,8 @@ --add-opens java.base/java.lang.ref=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.zip=ALL-UNNAMED + --add-opens=java.base/java.security=ALL-UNNAMED + --add-opens java.base/jdk.internal.misc=ALL-UNNAMED diff --git a/src/test/java/world/bentobox/chat/ChatPladdonTest.java b/src/test/java/world/bentobox/chat/ChatPladdonTest.java new file mode 100644 index 0000000..2c0b12d --- /dev/null +++ b/src/test/java/world/bentobox/chat/ChatPladdonTest.java @@ -0,0 +1,27 @@ +package world.bentobox.chat; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +/** + * Tests for the {@link ChatPladdon} class. + */ +public class ChatPladdonTest { + + @Test + public void testGetAddonReturnsChat() { + ChatPladdon pladdon = new ChatPladdon(); + assertNotNull(pladdon.getAddon()); + assertInstanceOf(Chat.class, pladdon.getAddon()); + } + + @Test + public void testGetAddonReturnsSameInstance() { + ChatPladdon pladdon = new ChatPladdon(); + // Should return the same instance on repeated calls + assertSame(pladdon.getAddon(), pladdon.getAddon()); + } +} diff --git a/src/test/java/world/bentobox/chat/CommonTestSetup.java b/src/test/java/world/bentobox/chat/CommonTestSetup.java new file mode 100644 index 0000000..1b6e165 --- /dev/null +++ b/src/test/java/world/bentobox/chat/CommonTestSetup.java @@ -0,0 +1,208 @@ +package world.bentobox.chat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mockbukkit.mockbukkit.MockBukkit; +import org.mockbukkit.mockbukkit.ServerMock; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import com.google.common.collect.ImmutableSet; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.Notifier; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; + +/** + * Common test setup for Chat addon tests. + * Provides a BentoBox plugin mock, common mocks, and MockBukkit server. + * Don't forget to call {@code super.setUp()} and {@code super.tearDown()} in subclasses. + */ +public abstract class CommonTestSetup { + + protected UUID uuid = UUID.randomUUID(); + + @Mock + protected Chat addon; + @Mock + protected Player player; + @Mock + protected PluginManager pim; + @Mock + protected ItemFactory itemFactory; + @Mock + protected Location location; + @Mock + protected World world; + @Mock + protected IslandWorldManager iwm; + @Mock + protected IslandsManager im; + @Mock + protected Island island; + @Mock + protected BentoBox plugin; + @Mock + protected Notifier notifier; + @Mock + protected BukkitScheduler sch; + @Mock + protected LocalesManager lm; + @Mock + protected CompositeCommand ic; + @Mock + protected GameModeAddon gameModeAddon; + @Mock + protected AddonsManager am; + @Mock + protected Settings settings; + + protected ServerMock server; + protected MockedStatic mockedBukkit; + protected AutoCloseable closeable; + + @BeforeEach + public void setUp() throws Exception { + closeable = MockitoAnnotations.openMocks(this); + server = MockBukkit.mock(); + + // Set up BentoBox singleton + WhiteBox.setInternalState(BentoBox.class, "instance", plugin); + + // Register the static mock for Bukkit + mockedBukkit = Mockito.mockStatic(Bukkit.class, Mockito.RETURNS_DEEP_STUBS); + mockedBukkit.when(Bukkit::getMinecraftVersion).thenReturn("1.21.11"); + mockedBukkit.when(Bukkit::getBukkitVersion).thenReturn("1.21.11-R0.1-SNAPSHOT"); + mockedBukkit.when(Bukkit::getPluginManager).thenReturn(pim); + mockedBukkit.when(Bukkit::getItemFactory).thenReturn(itemFactory); + mockedBukkit.when(Bukkit::getServer).thenReturn(server); + mockedBukkit.when(() -> Bukkit.getScheduler()).thenReturn(sch); + mockedBukkit.when(Bukkit::getOnlinePlayers).thenAnswer(invocation -> Collections.emptyList()); + + // World + when(world.toString()).thenReturn("world"); + when(world.getName()).thenReturn("BSkyBlock_world"); + + // Location + when(location.getWorld()).thenReturn(world); + when(location.getBlockX()).thenReturn(0); + when(location.getBlockY()).thenReturn(0); + when(location.getBlockZ()).thenReturn(0); + when(location.clone()).thenReturn(location); + + // Player + when(player.getUniqueId()).thenReturn(uuid); + when(player.getLocation()).thenReturn(location); + when(player.getWorld()).thenReturn(world); + when(player.getName()).thenReturn("tastybento"); + + User.setPlugin(plugin); + User.clearUsers(); + User.getInstance(player); + + // IWM + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(any(Location.class))).thenReturn(true); + when(iwm.inWorld(any(World.class))).thenReturn(true); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + + // Island Manager + when(plugin.getIslands()).thenReturn(im); + when(im.getProtectedIslandAt(any())).thenReturn(Optional.of(island)); + when(island.getOwner()).thenReturn(uuid); + when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid)); + when(island.onIsland(any())).thenReturn(true); + + // Locales & Placeholders + when(lm.get(any(), any())).thenAnswer( + (Answer) invocation -> invocation.getArgument(1, String.class)); + PlaceholdersManager phm = mock(PlaceholdersManager.class); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + when(phm.replacePlaceholders(any(), any())).thenAnswer( + (Answer) invocation -> invocation.getArgument(1, String.class)); + when(plugin.getLocalesManager()).thenReturn(lm); + + // Notifier + when(plugin.getNotifier()).thenReturn(notifier); + + // BentoBox settings + world.bentobox.bentobox.Settings bentoSettings = new world.bentobox.bentobox.Settings(); + when(plugin.getSettings()).thenReturn(bentoSettings); + + // AddonsManager + when(plugin.getAddonsManager()).thenReturn(am); + when(am.getGameModeAddons()).thenReturn(Collections.emptyList()); + + // Addon + when(addon.getPlugin()).thenReturn(plugin); + when(addon.getIslands()).thenReturn(im); + when(addon.getSettings()).thenReturn(settings); + + // Command + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + + // Settings + when(settings.isLogTeamChats()).thenReturn(false); + when(settings.isLogIslandChats()).thenReturn(false); + when(settings.getEventPriority()).thenReturn(org.bukkit.event.EventPriority.NORMAL); + + // Addon commandsManager + world.bentobox.bentobox.managers.CommandsManager cm = mock( + world.bentobox.bentobox.managers.CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + } + + @AfterEach + public void tearDown() throws Exception { + mockedBukkit.closeOnDemand(); + closeable.close(); + MockBukkit.unmock(); + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + deleteAll(new File("database")); + deleteAll(new File("database_backup")); + } + + protected static void deleteAll(File file) throws IOException { + if (file.exists()) { + Files.walk(file.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile) + .forEach(File::delete); + } + } +} diff --git a/src/test/java/world/bentobox/chat/SettingsTest.java b/src/test/java/world/bentobox/chat/SettingsTest.java new file mode 100644 index 0000000..6c70d74 --- /dev/null +++ b/src/test/java/world/bentobox/chat/SettingsTest.java @@ -0,0 +1,142 @@ +package world.bentobox.chat; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.event.EventPriority; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for the {@link Settings} class. + */ +public class SettingsTest { + + private Settings settings; + + @BeforeEach + public void setUp() { + // Set up static addon reference required by getEventPriority + Chat addonMock = mock(Chat.class); + Chat.addon = addonMock; + settings = new Settings(); + } + + @Test + public void testDefaultTeamChatGamemodes() { + List defaults = settings.getTeamChatGamemodes(); + assertNotNull(defaults); + assertTrue(defaults.contains("BSkyBlock")); + assertTrue(defaults.contains("AcidIsland")); + assertTrue(defaults.contains("CaveBlock")); + assertTrue(defaults.contains("SkyGrid")); + } + + @Test + public void testSetTeamChatGamemodes() { + List newList = Arrays.asList("BSkyBlock", "MyAddon"); + settings.setTeamChatGamemodes(newList); + assertEquals(newList, settings.getTeamChatGamemodes()); + } + + @Test + public void testDefaultIslandChatGamemodes() { + List defaults = settings.getIslandChatGamemodes(); + assertNotNull(defaults); + assertTrue(defaults.contains("BSkyBlock")); + assertTrue(defaults.contains("AcidIsland")); + } + + @Test + public void testSetIslandChatGamemodes() { + List newList = Arrays.asList("BSkyBlock"); + settings.setIslandChatGamemodes(newList); + assertEquals(newList, settings.getIslandChatGamemodes()); + } + + @Test + public void testDefaultLogTeamChats() { + assertFalse(settings.isLogTeamChats()); + } + + @Test + public void testSetLogTeamChats() { + settings.setLogTeamChats(true); + assertTrue(settings.isLogTeamChats()); + settings.setLogTeamChats(false); + assertFalse(settings.isLogTeamChats()); + } + + @Test + public void testDefaultLogIslandChats() { + assertFalse(settings.isLogIslandChats()); + } + + @Test + public void testSetLogIslandChats() { + settings.setLogIslandChats(true); + assertTrue(settings.isLogIslandChats()); + settings.setLogIslandChats(false); + assertFalse(settings.isLogIslandChats()); + } + + @Test + public void testDefaultEventPriority() { + // Default is "normal" which maps to NORMAL + assertEquals(EventPriority.NORMAL, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityEnum() { + settings.setEventPriority(EventPriority.HIGH); + assertEquals(EventPriority.HIGH, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityString() { + settings.setEventPriority("highest"); + assertEquals(EventPriority.HIGHEST, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityStringLow() { + settings.setEventPriority("low"); + assertEquals(EventPriority.LOW, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityStringLowest() { + settings.setEventPriority("lowest"); + assertEquals(EventPriority.LOWEST, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityStringMonitor() { + settings.setEventPriority("monitor"); + assertEquals(EventPriority.MONITOR, settings.getEventPriority()); + } + + @Test + public void testSetEventPriorityStringInvalid() { + // Invalid value should fall back to NORMAL and log an error + settings.setEventPriority("invalid_priority"); + assertEquals(EventPriority.NORMAL, settings.getEventPriority()); + } + + @Test + public void testDefaultChatGamemode() { + assertEquals("", settings.getDefaultChatGamemode()); + } + + @Test + public void testSetDefaultChatGamemode() { + settings.setDefaultChatGamemode("BSkyBlock"); + assertEquals("BSkyBlock", settings.getDefaultChatGamemode()); + } +} diff --git a/src/test/java/world/bentobox/chat/WhiteBox.java b/src/test/java/world/bentobox/chat/WhiteBox.java new file mode 100644 index 0000000..cf0cad0 --- /dev/null +++ b/src/test/java/world/bentobox/chat/WhiteBox.java @@ -0,0 +1,24 @@ +package world.bentobox.chat; + +/** + * Utility class for setting private/static fields via reflection in tests. + */ +public class WhiteBox { + + /** + * Sets the value of a private static field using Java Reflection. + * @param targetClass The class containing the static field. + * @param fieldName The name of the private static field. + * @param value The value to set the field to. + */ + public static void setInternalState(Class targetClass, String fieldName, Object value) { + try { + java.lang.reflect.Field field = targetClass.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(null, value); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException( + "Failed to set static field '" + fieldName + "' on class " + targetClass.getName(), e); + } + } +} diff --git a/src/test/java/world/bentobox/chat/commands/admin/AdminIslandChatSpyCommandTest.java b/src/test/java/world/bentobox/chat/commands/admin/AdminIslandChatSpyCommandTest.java new file mode 100644 index 0000000..90a0b47 --- /dev/null +++ b/src/test/java/world/bentobox/chat/commands/admin/AdminIslandChatSpyCommandTest.java @@ -0,0 +1,67 @@ +package world.bentobox.chat.commands.admin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link AdminIslandChatSpyCommand} class. + */ +public class AdminIslandChatSpyCommandTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + @Mock + private User user; + + private AdminIslandChatSpyCommand cmd; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + when(user.getUniqueId()).thenReturn(uuid); + cmd = new AdminIslandChatSpyCommand(addon, ic, "chatspy"); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testSetup() { + assertEquals("bskyblock.chat.spy", cmd.getPermission()); + assertTrue(cmd.isOnlyPlayer()); + assertEquals("chat.island-chat.spy.description", cmd.getDescription()); + } + + @Test + public void testExecuteToggleSpyOn() { + when(chatListener.toggleIslandSpy(uuid)).thenReturn(true); + assertTrue(cmd.execute(user, "chatspy", Collections.emptyList())); + verify(user).sendMessage("chat.island-chat.spy.spy-on"); + } + + @Test + public void testExecuteToggleSpyOff() { + when(chatListener.toggleIslandSpy(uuid)).thenReturn(false); + assertTrue(cmd.execute(user, "chatspy", Collections.emptyList())); + verify(user).sendMessage("chat.island-chat.spy.spy-off"); + } +} diff --git a/src/test/java/world/bentobox/chat/commands/admin/AdminTeamChatSpyCommandTest.java b/src/test/java/world/bentobox/chat/commands/admin/AdminTeamChatSpyCommandTest.java new file mode 100644 index 0000000..217b6ba --- /dev/null +++ b/src/test/java/world/bentobox/chat/commands/admin/AdminTeamChatSpyCommandTest.java @@ -0,0 +1,67 @@ +package world.bentobox.chat.commands.admin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link AdminTeamChatSpyCommand} class. + */ +public class AdminTeamChatSpyCommandTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + @Mock + private User user; + + private AdminTeamChatSpyCommand cmd; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + when(user.getUniqueId()).thenReturn(uuid); + cmd = new AdminTeamChatSpyCommand(addon, ic, "teamchatspy"); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testSetup() { + assertEquals("bskyblock.chat.spy", cmd.getPermission()); + assertTrue(cmd.isOnlyPlayer()); + assertEquals("chat.team-chat.spy.description", cmd.getDescription()); + } + + @Test + public void testExecuteToggleSpyOn() { + when(chatListener.toggleSpy(uuid)).thenReturn(true); + assertTrue(cmd.execute(user, "teamchatspy", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.spy.spy-on"); + } + + @Test + public void testExecuteToggleSpyOff() { + when(chatListener.toggleSpy(uuid)).thenReturn(false); + assertTrue(cmd.execute(user, "teamchatspy", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.spy.spy-off"); + } +} diff --git a/src/test/java/world/bentobox/chat/commands/island/IslandChatCommandTest.java b/src/test/java/world/bentobox/chat/commands/island/IslandChatCommandTest.java new file mode 100644 index 0000000..4b9ddcb --- /dev/null +++ b/src/test/java/world/bentobox/chat/commands/island/IslandChatCommandTest.java @@ -0,0 +1,109 @@ +package world.bentobox.chat.commands.island; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link IslandChatCommand} class. + */ +public class IslandChatCommandTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + @Mock + private User user; + + private IslandChatCommand cmd; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getLocation()).thenReturn(location); + cmd = new IslandChatCommand(addon, ic, "chat"); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testSetup() { + assertEquals("bskyblock.chat.island-chat", cmd.getPermission()); + assertTrue(cmd.isOnlyPlayer()); + assertEquals("chat.island-chat.description", cmd.getDescription()); + } + + @Test + public void testCanExecuteNoIsland() { + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + assertFalse(cmd.canExecute(user, "chat", Collections.emptyList())); + } + + @Test + public void testCanExecuteWithIsland() { + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + assertTrue(cmd.canExecute(user, "chat", Collections.emptyList())); + } + + @Test + public void testExecuteToggleOn() { + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + cmd.canExecute(user, "chat", Collections.emptyList()); // sets island field + when(chatListener.toggleIslandChat(any(Island.class), eq(player))).thenReturn(true); + assertTrue(cmd.execute(user, "chat", Collections.emptyList())); + verify(user).sendMessage("chat.island-chat.island-on"); + } + + @Test + public void testExecuteToggleOff() { + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + cmd.canExecute(user, "chat", Collections.emptyList()); + when(chatListener.toggleIslandChat(any(Island.class), eq(player))).thenReturn(false); + assertTrue(cmd.execute(user, "chat", Collections.emptyList())); + verify(user).sendMessage("chat.island-chat.island-off"); + } + + @Test + public void testExecuteWithArgs() { + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + cmd.canExecute(user, "chat", Collections.emptyList()); + assertTrue(cmd.execute(user, "chat", List.of("hello", "island"))); + verify(chatListener).islandChat(any(Island.class), eq(player), eq("hello island")); + verify(chatListener, never()).toggleIslandChat(any(), any()); + } + + @Test + public void testExecuteWithSingleArg() { + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + cmd.canExecute(user, "chat", Collections.emptyList()); + assertTrue(cmd.execute(user, "chat", List.of("hi"))); + verify(chatListener).islandChat(any(Island.class), eq(player), eq("hi")); + } +} diff --git a/src/test/java/world/bentobox/chat/commands/island/IslandTeamChatCommandTest.java b/src/test/java/world/bentobox/chat/commands/island/IslandTeamChatCommandTest.java new file mode 100644 index 0000000..3749310 --- /dev/null +++ b/src/test/java/world/bentobox/chat/commands/island/IslandTeamChatCommandTest.java @@ -0,0 +1,105 @@ +package world.bentobox.chat.commands.island; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; + +import org.bukkit.World; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link IslandTeamChatCommand} class. + */ +public class IslandTeamChatCommandTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + @Mock + private User user; + + private IslandTeamChatCommand cmd; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getWorld()).thenReturn(world); + cmd = new IslandTeamChatCommand(addon, ic, "teamchat"); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testSetup() { + assertEquals("bskyblock.chat.team-chat", cmd.getPermission()); + assertTrue(cmd.isOnlyPlayer()); + assertEquals("chat.team-chat.description", cmd.getDescription()); + } + + @Test + public void testCanExecuteNoTeam() { + when(im.inTeam(any(World.class), eq(uuid))).thenReturn(false); + assertFalse(cmd.canExecute(user, "teamchat", Collections.emptyList())); + verify(user).sendMessage("general.errors.no-team"); + } + + @Test + public void testCanExecuteInTeam() { + when(im.inTeam(any(World.class), eq(uuid))).thenReturn(true); + assertTrue(cmd.canExecute(user, "teamchat", Collections.emptyList())); + verify(user, never()).sendMessage(anyString()); + } + + @Test + public void testExecuteToggleOn() { + when(chatListener.togglePlayerTeamChat(uuid)).thenReturn(true); + assertTrue(cmd.execute(user, "teamchat", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.chat-on"); + } + + @Test + public void testExecuteToggleOff() { + when(chatListener.togglePlayerTeamChat(uuid)).thenReturn(false); + assertTrue(cmd.execute(user, "teamchat", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.chat-off"); + } + + @Test + public void testExecuteWithArgs() { + // When args are provided, send directly to team chat + assertTrue(cmd.execute(user, "teamchat", List.of("hello", "world"))); + verify(chatListener).teamChat(any(World.class), eq(player), eq("hello world")); + verify(chatListener, never()).togglePlayerTeamChat(any()); + } + + @Test + public void testExecuteWithSingleArg() { + assertTrue(cmd.execute(user, "teamchat", List.of("hi"))); + verify(chatListener).teamChat(any(World.class), eq(player), eq("hi")); + } +} diff --git a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java new file mode 100644 index 0000000..2a5ac2f --- /dev/null +++ b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java @@ -0,0 +1,320 @@ +package world.bentobox.chat.listeners; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import com.google.common.collect.ImmutableSet; + +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.chat.CommonTestSetup; + +/** + * Tests for the {@link ChatListener} class. + */ +@SuppressWarnings("deprecation") +public class ChatListenerTest extends CommonTestSetup { + + private ChatListener listener; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(true); + when(addon.getChatWorld()).thenReturn(Optional.empty()); + listener = new ChatListener(addon); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + // ----------------------------------------------------------------------- + // togglePlayerTeamChat tests + // ----------------------------------------------------------------------- + + @Test + public void testTogglePlayerTeamChatOn() { + UUID uid = UUID.randomUUID(); + assertTrue(listener.togglePlayerTeamChat(uid), + "First toggle should turn team chat ON"); + } + + @Test + public void testTogglePlayerTeamChatOff() { + UUID uid = UUID.randomUUID(); + listener.togglePlayerTeamChat(uid); // ON + assertFalse(listener.togglePlayerTeamChat(uid), + "Second toggle should turn team chat OFF"); + } + + @Test + public void testIsTeamChatFalseByDefault() { + assertFalse(listener.isTeamChat(UUID.randomUUID())); + } + + @Test + public void testIsTeamChatTrueAfterToggle() { + UUID uid = UUID.randomUUID(); + listener.togglePlayerTeamChat(uid); + assertTrue(listener.isTeamChat(uid)); + } + + // ----------------------------------------------------------------------- + // toggleSpy tests + // ----------------------------------------------------------------------- + + @Test + public void testToggleSpyOn() { + assertTrue(listener.toggleSpy(uuid)); + } + + @Test + public void testToggleSpyOff() { + listener.toggleSpy(uuid); // ON + assertFalse(listener.toggleSpy(uuid)); + } + + // ----------------------------------------------------------------------- + // toggleIslandSpy tests + // ----------------------------------------------------------------------- + + @Test + public void testToggleIslandSpyOn() { + assertTrue(listener.toggleIslandSpy(uuid)); + } + + @Test + public void testToggleIslandSpyOff() { + listener.toggleIslandSpy(uuid); // ON + assertFalse(listener.toggleIslandSpy(uuid)); + } + + // ----------------------------------------------------------------------- + // toggleIslandChat tests + // ----------------------------------------------------------------------- + + @Test + public void testToggleIslandChatOn() { + assertTrue(listener.toggleIslandChat(island, player)); + } + + @Test + public void testToggleIslandChatOff() { + listener.toggleIslandChat(island, player); // ON + assertFalse(listener.toggleIslandChat(island, player)); + } + + // ----------------------------------------------------------------------- + // isChat tests + // ----------------------------------------------------------------------- + + @Test + public void testIsChatFalseByDefault() { + assertFalse(listener.isChat(uuid)); + } + + @Test + public void testIsChatFalseWhenPlayerOffline() { + // Player is not online in server mock by default for an unknown UUID + assertFalse(listener.isChat(UUID.randomUUID())); + } + + @Test + public void testIsChatTrueAfterToggle() { + mockedBukkit.when(() -> org.bukkit.Bukkit.getPlayer(uuid)).thenReturn(player); + listener.toggleIslandChat(island, player); + assertTrue(listener.isChat(uuid)); + } + + // ----------------------------------------------------------------------- + // onChat tests + // ----------------------------------------------------------------------- + + @Test + public void testOnChatNotInRegisteredWorld() { + // Not in a registered game world + when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(false); + when(addon.getChatWorld()).thenReturn(Optional.empty()); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello", Collections.emptySet()); + listener.onChat(event); + assertFalse(event.isCancelled()); + } + + @Test + public void testOnChatTeamChatActive() { + // Player has team chat enabled and is in a team + listener.togglePlayerTeamChat(uuid); + when(im.inTeam(any(World.class), any(UUID.class))).thenReturn(true); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello team!", Collections.emptySet()); + listener.onChat(event); + // Chat should be cancelled (intercepted for team chat) + assertTrue(event.isCancelled()); + } + + @Test + public void testOnChatTeamChatNotInTeam() { + // Player has team chat enabled but is NOT in a team + listener.togglePlayerTeamChat(uuid); + when(im.inTeam(any(World.class), any(UUID.class))).thenReturn(false); + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello!", Collections.emptySet()); + listener.onChat(event); + // Not cancelled because not in team + assertFalse(event.isCancelled()); + } + + @Test + public void testOnChatIslandChatActive() { + // Player is in island chat + listener.toggleIslandChat(island, player); + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello island!", Collections.emptySet()); + listener.onChat(event); + assertTrue(event.isCancelled()); + } + + @Test + public void testOnChatIslandChatPlayerNotRegistered() { + // Island is in the map but this player is not registered to island chat + Player otherPlayer = mock(Player.class); + when(otherPlayer.getUniqueId()).thenReturn(UUID.randomUUID()); + when(otherPlayer.getWorld()).thenReturn(world); + when(otherPlayer.getLocation()).thenReturn(location); + + listener.toggleIslandChat(island, otherPlayer); // Register different player + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello!", Collections.emptySet()); + listener.onChat(event); + // This player (uuid) is not in the island chatters set, so not cancelled + assertFalse(event.isCancelled()); + } + + // ----------------------------------------------------------------------- + // teamChat tests + // ----------------------------------------------------------------------- + + @Test + public void testTeamChatNoIsland() { + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + // Should not throw, just return silently + listener.teamChat(world, player, "test message"); + // No verification needed - just ensures no NPE + } + + @Test + public void testTeamChatWithIsland() { + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(island); + when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid)); + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); + + // Should not throw + listener.teamChat(world, player, "team message"); + } + + @Test + public void testTeamChatLogsWhenEnabled() { + when(settings.isLogTeamChats()).thenReturn(true); + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(island); + when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid)); + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); + + listener.teamChat(world, player, "logged message"); + verify(addon).log("[Team Chat Log] tastybento: logged message"); + } + + @Test + public void testTeamChatDoesNotLogWhenDisabled() { + when(settings.isLogTeamChats()).thenReturn(false); + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(island); + when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid)); + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); + + listener.teamChat(world, player, "silent message"); + verify(addon, never()).log(any()); + } + + // ----------------------------------------------------------------------- + // islandChat tests + // ----------------------------------------------------------------------- + + @Test + public void testIslandChatLogsWhenEnabled() { + when(settings.isLogTeamChats()).thenReturn(true); + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); + + listener.islandChat(island, player, "island log message"); + verify(addon).log("[Team Chat Log] tastybento: island log message"); + } + + @Test + public void testIslandChatDoesNotLogWhenDisabled() { + when(settings.isLogTeamChats()).thenReturn(false); + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); + + listener.islandChat(island, player, "island silent message"); + verify(addon, never()).log(any()); + } + + @Test + public void testIslandChatSendsToIslandMembers() { + mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.singletonList(player)); + + // Should not throw and should complete without errors + listener.islandChat(island, player, "hello island"); + } + + // ----------------------------------------------------------------------- + // Event handler tests + // ----------------------------------------------------------------------- + + @Test + public void testOnLeaveRemovesTeamChatUser() { + listener.togglePlayerTeamChat(uuid); + assertTrue(listener.isTeamChat(uuid)); + + world.bentobox.bentobox.api.events.team.TeamLeaveEvent leaveEvent = + mock(world.bentobox.bentobox.api.events.team.TeamLeaveEvent.class); + when(leaveEvent.getPlayerUUID()).thenReturn(uuid); + listener.onLeave(leaveEvent); + + assertFalse(listener.isTeamChat(uuid)); + } + + @Test + public void testOnKickRemovesTeamChatUser() { + listener.togglePlayerTeamChat(uuid); + assertTrue(listener.isTeamChat(uuid)); + + world.bentobox.bentobox.api.events.team.TeamKickEvent kickEvent = + mock(world.bentobox.bentobox.api.events.team.TeamKickEvent.class); + when(kickEvent.getPlayerUUID()).thenReturn(uuid); + listener.onKick(kickEvent); + + assertFalse(listener.isTeamChat(uuid)); + } +} diff --git a/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java b/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java new file mode 100644 index 0000000..34e08d8 --- /dev/null +++ b/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java @@ -0,0 +1,97 @@ +package world.bentobox.chat.requesthandlers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link IsTeamChatHandler} class. + */ +public class IsTeamChatHandlerTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + private IsTeamChatHandler handler; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + handler = new IsTeamChatHandler(addon); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testHandleLabel() { + assertEquals("isTeamChat", handler.getLabel()); + } + + @Test + public void testHandleNullMap() { + assertFalse((Boolean) handler.handle(null)); + } + + @Test + public void testHandleEmptyMap() { + assertFalse((Boolean) handler.handle(new HashMap<>())); + } + + @Test + public void testHandleMissingUuid() { + Map map = new HashMap<>(); + map.put("some_key", "some_value"); + assertFalse((Boolean) handler.handle(map)); + } + + @Test + public void testHandleNullUuid() { + Map map = new HashMap<>(); + map.put("uuid", null); + assertFalse((Boolean) handler.handle(map)); + } + + @Test + public void testHandleWrongTypeUuid() { + Map map = new HashMap<>(); + map.put("uuid", "not-a-uuid-object"); + assertFalse((Boolean) handler.handle(map)); + } + + @Test + public void testHandlePlayerNotInTeamChat() { + UUID testUuid = UUID.randomUUID(); + when(chatListener.isTeamChat(testUuid)).thenReturn(false); + Map map = new HashMap<>(); + map.put("uuid", testUuid); + assertFalse((Boolean) handler.handle(map)); + } + + @Test + public void testHandlePlayerInTeamChat() { + UUID testUuid = UUID.randomUUID(); + when(chatListener.isTeamChat(testUuid)).thenReturn(true); + Map map = new HashMap<>(); + map.put("uuid", testUuid); + assertTrue((Boolean) handler.handle(map)); + } +} From fd25121fd81366886596e6061b1c0d28a1cdca36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:05:40 +0000 Subject: [PATCH 07/36] Fix --add-opens flag format consistency in pom.xml Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6fb1e6b..9fc0d44 100644 --- a/pom.xml +++ b/pom.xml @@ -270,7 +270,7 @@ --add-opens java.base/java.lang.ref=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.zip=ALL-UNNAMED - --add-opens=java.base/java.security=ALL-UNNAMED + --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED From adbdbafebc9ecd79105ef9eaeb2bfe86ead7b379 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:13:47 +0000 Subject: [PATCH 08/36] Initial plan From d33662c21ef6ec85e4d2ae4a5e2568f0f2f7363f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:16:22 +0000 Subject: [PATCH 09/36] Add GitHub Actions workflow to build and test on push/PR to develop Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- .github/workflows/build.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c73f4e4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,32 @@ +name: Build and Test + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +jobs: + build-and-test: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Build with Maven + run: mvn -B clean compile + + - name: Run tests + run: mvn -B test From 86ac41c53f16f1dcf40f23ab5497ca6ebf50a041 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 16 Mar 2026 20:57:44 -0700 Subject: [PATCH 10/36] Fix build: update Maven repos, downgrade BentoBox version, and fix failing tests Updated pom.xml repositories to match current BentoBox conventions (jitpack.io for MockBukkit, proper release/snapshot config). Downgraded BentoBox from 3.10.2 (unavailable) to 3.10.0. Fixed four test failures: lowercased label assertion in IsTeamChatHandlerTest, JavaPlugin classloader issues in ChatPladdonTest, and missing Player.Spigot mock in ChatListenerTest. Added instance field setter and Unsafe-based newUninitializedInstance to WhiteBox. Added CLAUDE.md. Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 38 ++++++++++++++++ pom.xml | 33 +++++++------- .../world/bentobox/chat/ChatPladdonTest.java | 28 +++++++++--- .../java/world/bentobox/chat/WhiteBox.java | 43 +++++++++++++++++++ .../chat/listeners/ChatListenerTest.java | 2 + .../IsTeamChatHandlerTest.java | 2 +- 6 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bad86ab --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,38 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Chat is a BentoBox addon for Minecraft (Paper/Spigot) that provides Team Chat and Island Chat for island-type game modes (BSkyBlock, AcidIsland, CaveBlock, SkyGrid). It requires Java 21 and targets Paper 1.21.x. + +## Build Commands + +```bash +mvn clean package # Build (default goal) +mvn test # Run all tests +mvn -Dtest=ChatListenerTest test # Run a single test class +mvn -Dtest=ChatListenerTest#onChat test # Run a single test method +``` + +The build produces `target/Chat-{version}-SNAPSHOT-LOCAL.jar`. + +## Architecture + +This is a **BentoBox Pladdon** (plugin-addon): + +- `ChatPladdon` — Entry point loaded by BentoBox's Pladdon system, creates the `Chat` addon instance +- `Chat` — Main addon class. On enable: loads `Settings` from config.yml, registers commands per game mode, creates `ChatListener`, and registers the `IsTeamChatHandler` request handler +- `ChatListener` — Core logic. Implements both `Listener` and `EventExecutor`. Intercepts `AsyncPlayerChatEvent` (registered manually with configurable priority from Settings). Maintains in-memory sets for team chat users, island chatters, team spies, and island spies. All toggle state is held in memory (not persisted) +- `Settings` — BentoBox `ConfigObject` stored at `addons/Chat/config.yml`. Controls which game modes have team/island chat, logging, event priority, and default chat game mode +- `IsTeamChatHandler` — BentoBox request handler that lets other addons query if a player has team chat enabled + +Commands follow BentoBox's `CompositeCommand` pattern and are registered dynamically onto each game mode's player/admin command trees: +- Player: `IslandChatCommand` (chat), `IslandTeamChatCommand` (teamchat) +- Admin: `AdminIslandChatSpyCommand` (chatspy), `AdminTeamChatSpyCommand` (teamchatspy) + +## Testing + +Tests use JUnit 5 + Mockito + MockBukkit. The `CommonTestSetup` base class provides standard mocks for BentoBox, Bukkit, Player, World, Island, etc. New test classes should extend `CommonTestSetup` and call `super.setUp()`/`super.tearDown()`. The `WhiteBox` utility sets private/static fields via reflection for test setup. + +Localization strings are defined in `src/main/resources/locales/` (en-US.yml is the primary locale). diff --git a/pom.xml b/pom.xml index 9fc0d44..8d8f568 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ v1.21-SNAPSHOT 1.21.11-R0.1-SNAPSHOT - 3.10.2 + 3.10.0 ${build.version}-SNAPSHOT @@ -117,29 +117,26 @@ + - papermc - https://repo.papermc.io/repository/maven-public/ - - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots + jitpack.io + https://jitpack.io + true + true + bentoboxworld - https://repo.codemc.org/repository/bentoboxworld - - - codemc-repo - https://repo.codemc.org/repository/maven-public/ - - - codemc-public - https://repo.codemc.org/repository/maven-public/ + https://repo.codemc.org/repository/bentoboxworld/ + true + false + - codemc-snapshots - https://repo.codemc.org/repository/maven-snapshots/ + papermc + https://repo.papermc.io/repository/maven-public/ + true + true diff --git a/src/test/java/world/bentobox/chat/ChatPladdonTest.java b/src/test/java/world/bentobox/chat/ChatPladdonTest.java index 2c0b12d..94ac59a 100644 --- a/src/test/java/world/bentobox/chat/ChatPladdonTest.java +++ b/src/test/java/world/bentobox/chat/ChatPladdonTest.java @@ -1,26 +1,44 @@ package world.bentobox.chat; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for the {@link ChatPladdon} class. */ -public class ChatPladdonTest { +public class ChatPladdonTest extends CommonTestSetup { + + private ChatPladdon pladdon; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + pladdon = WhiteBox.newUninitializedInstance(ChatPladdon.class); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } @Test public void testGetAddonReturnsChat() { - ChatPladdon pladdon = new ChatPladdon(); + // Set a Chat instance via reflection to avoid classloader issues + Chat chat = WhiteBox.newUninitializedInstance(Chat.class); + WhiteBox.setInternalState(pladdon, "addon", chat); assertNotNull(pladdon.getAddon()); - assertInstanceOf(Chat.class, pladdon.getAddon()); } @Test public void testGetAddonReturnsSameInstance() { - ChatPladdon pladdon = new ChatPladdon(); + Chat chat = WhiteBox.newUninitializedInstance(Chat.class); + WhiteBox.setInternalState(pladdon, "addon", chat); // Should return the same instance on repeated calls assertSame(pladdon.getAddon(), pladdon.getAddon()); } diff --git a/src/test/java/world/bentobox/chat/WhiteBox.java b/src/test/java/world/bentobox/chat/WhiteBox.java index cf0cad0..8c22979 100644 --- a/src/test/java/world/bentobox/chat/WhiteBox.java +++ b/src/test/java/world/bentobox/chat/WhiteBox.java @@ -21,4 +21,47 @@ public static void setInternalState(Class targetClass, String fieldName, Obje "Failed to set static field '" + fieldName + "' on class " + targetClass.getName(), e); } } + + /** + * Sets the value of a private instance field using Java Reflection. + * @param target The object instance containing the field. + * @param fieldName The name of the private field. + * @param value The value to set the field to. + */ + public static void setInternalState(Object target, String fieldName, Object value) { + try { + java.lang.reflect.Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException( + "Failed to set field '" + fieldName + "' on " + target.getClass().getName(), e); + } + } + + /** + * Creates an instance of a class without calling its constructor. + * Useful for classes that require special classloaders (e.g., JavaPlugin subclasses). + * @param clazz The class to instantiate. + * @param The type of the class. + * @return A new uninitialized instance. + */ + @SuppressWarnings("unchecked") + public static T newUninitializedInstance(Class clazz) { + try { + return (T) getUnsafe().allocateInstance(clazz); + } catch (Exception e) { + throw new RuntimeException("Failed to create uninitialized instance of " + clazz.getName(), e); + } + } + + private static sun.misc.Unsafe getUnsafe() { + try { + java.lang.reflect.Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + } catch (Exception e) { + throw new RuntimeException("Failed to get Unsafe instance", e); + } + } } diff --git a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java index 2a5ac2f..b270d30 100644 --- a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java +++ b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java @@ -282,6 +282,8 @@ public void testIslandChatDoesNotLogWhenDisabled() { @Test public void testIslandChatSendsToIslandMembers() { + Player.Spigot spigot = mock(Player.Spigot.class); + when(player.spigot()).thenReturn(spigot); mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.singletonList(player)); // Should not throw and should complete without errors diff --git a/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java b/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java index 34e08d8..1852bd7 100644 --- a/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java +++ b/src/test/java/world/bentobox/chat/requesthandlers/IsTeamChatHandlerTest.java @@ -43,7 +43,7 @@ public void tearDown() throws Exception { @Test public void testHandleLabel() { - assertEquals("isTeamChat", handler.getLabel()); + assertEquals("isteamchat", handler.getLabel()); } @Test From aab11c6656b7856e8741689c569de3c040af2cf4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:02:20 +0000 Subject: [PATCH 11/36] Initial plan From 60b812b31ba3961b5bd9569599e551fd99625b42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:04:43 +0000 Subject: [PATCH 12/36] Initial plan From 1cf1bf02a118a9fd0b76518162c24335d022607b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:08:07 +0000 Subject: [PATCH 13/36] Add team mute command (/is muteteamchat) to allow players to mute team chat messages Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- src/main/java/world/bentobox/chat/Chat.java | 2 + .../island/IslandTeamMuteCommand.java | 49 ++++++++++ .../bentobox/chat/listeners/ChatListener.java | 32 +++++++ src/main/resources/locales/en-US.yml | 4 + .../island/IslandTeamMuteCommandTest.java | 89 +++++++++++++++++++ .../chat/listeners/ChatListenerTest.java | 57 ++++++++++++ 6 files changed, 233 insertions(+) create mode 100644 src/main/java/world/bentobox/chat/commands/island/IslandTeamMuteCommand.java create mode 100644 src/test/java/world/bentobox/chat/commands/island/IslandTeamMuteCommandTest.java diff --git a/src/main/java/world/bentobox/chat/Chat.java b/src/main/java/world/bentobox/chat/Chat.java index a03cd87..b15a45c 100644 --- a/src/main/java/world/bentobox/chat/Chat.java +++ b/src/main/java/world/bentobox/chat/Chat.java @@ -15,6 +15,7 @@ import world.bentobox.chat.commands.admin.AdminTeamChatSpyCommand; import world.bentobox.chat.commands.island.IslandChatCommand; import world.bentobox.chat.commands.island.IslandTeamChatCommand; +import world.bentobox.chat.commands.island.IslandTeamMuteCommand; import world.bentobox.chat.listeners.ChatListener; import world.bentobox.chat.requesthandlers.IsTeamChatHandler; @@ -67,6 +68,7 @@ private void setupCommands() { if (settings.getTeamChatGamemodes().contains(gameModeAddon.getDescription().getName())) { log("Hooking team chat into " + gameModeAddon.getDescription().getName()); gameModeAddon.getPlayerCommand().ifPresent(c -> new IslandTeamChatCommand(this, c, "teamchat")); + gameModeAddon.getPlayerCommand().ifPresent(c -> new IslandTeamMuteCommand(this, c, "muteteamchat")); gameModeAddon.getAdminCommand().ifPresent(c -> new AdminTeamChatSpyCommand(this, c, "teamchatspy")); registeredGameModes.add(gameModeAddon); } diff --git a/src/main/java/world/bentobox/chat/commands/island/IslandTeamMuteCommand.java b/src/main/java/world/bentobox/chat/commands/island/IslandTeamMuteCommand.java new file mode 100644 index 0000000..039b02c --- /dev/null +++ b/src/main/java/world/bentobox/chat/commands/island/IslandTeamMuteCommand.java @@ -0,0 +1,49 @@ +package world.bentobox.chat.commands.island; + +import java.util.List; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.chat.Chat; + +/** + * Allows a player to mute/unmute team chat messages. + * When muted, the player will not receive team chat messages. + * @author tastybento + */ +public class IslandTeamMuteCommand extends CompositeCommand { + + public IslandTeamMuteCommand(Addon addon, CompositeCommand parent, String label) { + super(addon, parent, label, "mtc"); + } + + @Override + public void setup() { + this.setPermission("chat.team-chat.mute"); + this.setDescription("chat.team-chat.mute.description"); + this.setOnlyPlayer(true); + setConfigurableRankCommand(); + } + + @Override + public boolean canExecute(User user, String label, List args) { + boolean hasTeam = this.getIslands().inTeam(getWorld(), user.getUniqueId()); + if (!hasTeam) { + user.sendMessage("general.errors.no-team"); + } + return hasTeam; + } + + @Override + public boolean execute(User user, String label, List args) { + Chat addon = this.getAddon(); + + if (addon.getListener().toggleMuteTeamChat(user.getUniqueId())) { + user.sendMessage("chat.team-chat.mute.mute-on"); + } else { + user.sendMessage("chat.team-chat.mute.mute-off"); + } + return true; + } +} diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 7e3cca0..fc4e969 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -37,6 +37,8 @@ public class ChatListener implements Listener, EventExecutor { // List of which users are spying or not on team and island chat private final Set spies; private final Set islandSpies; + // List of which users have muted team chat + private final Set teamChatMuted; public ChatListener(Chat addon) { this.teamChatUsers = new HashSet<>(); @@ -45,6 +47,8 @@ public ChatListener(Chat addon) { // Initialize spies spies = new HashSet<>(); islandSpies = new HashSet<>(); + // Initialize muted + teamChatMuted = new HashSet<>(); } @Override @@ -100,12 +104,14 @@ public void onChat(final AsyncPlayerChatEvent e) { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onLeave(TeamLeaveEvent e) { teamChatUsers.remove(e.getPlayerUUID()); + teamChatMuted.remove(e.getPlayerUUID()); } // Removes player from TeamChat set if he was kicked from the island @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onKick(TeamKickEvent e) { teamChatUsers.remove(e.getPlayerUUID()); + teamChatMuted.remove(e.getPlayerUUID()); } public void islandChat(Island i, Player player, String message) { @@ -135,6 +141,8 @@ public void teamChat(World w, final Player player, String message) { .map(User::getInstance) // Filter for online only .filter(User::isOnline) + // Filter out muted players + .filter(target -> !teamChatMuted.contains(target.getUniqueId())) // Send the message to them .forEach(target -> target.sendMessage("chat.team-chat.syntax", TextVariables.NAME, player.getName(), MESSAGE, message)); // Log if required @@ -235,4 +243,28 @@ public boolean toggleIslandChat(Island island, Player player) { } } + /** + * Toggle player's team chat mute state + * @param playerUUID - player's uuid + * @return true if team chat is now muted, otherwise false + */ + public boolean toggleMuteTeamChat(UUID playerUUID) { + if (teamChatMuted.contains(playerUUID)) { + teamChatMuted.remove(playerUUID); + return false; + } else { + teamChatMuted.add(playerUUID); + return true; + } + } + + /** + * Whether the player has muted team chat or not + * @param playerUUID - the player's UUID + * @return true if team chat is muted + */ + public boolean isMutedTeamChat(UUID playerUUID) { + return this.teamChatMuted.contains(playerUUID); + } + } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 1a9a73b..1b9504a 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a Team chat on." chat-off: "&a Team chat off." syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "toggles muting team chat messages" + mute-on: "&c Team chat muted." + mute-off: "&a Team chat unmuted." spy: description: "toggles spying on all team chats" spy-on: "&a Team chat spy on." diff --git a/src/test/java/world/bentobox/chat/commands/island/IslandTeamMuteCommandTest.java b/src/test/java/world/bentobox/chat/commands/island/IslandTeamMuteCommandTest.java new file mode 100644 index 0000000..20a4b0c --- /dev/null +++ b/src/test/java/world/bentobox/chat/commands/island/IslandTeamMuteCommandTest.java @@ -0,0 +1,89 @@ +package world.bentobox.chat.commands.island; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.bukkit.World; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.chat.CommonTestSetup; +import world.bentobox.chat.listeners.ChatListener; + +/** + * Tests for the {@link IslandTeamMuteCommand} class. + */ +public class IslandTeamMuteCommandTest extends CommonTestSetup { + + @Mock + private ChatListener chatListener; + + @Mock + private User user; + + private IslandTeamMuteCommand cmd; + + @Override + @BeforeEach + public void setUp() throws Exception { + super.setUp(); + when(addon.getListener()).thenReturn(chatListener); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getWorld()).thenReturn(world); + cmd = new IslandTeamMuteCommand(addon, ic, "muteteamchat"); + } + + @Override + @AfterEach + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testSetup() { + assertEquals("bskyblock.chat.team-chat.mute", cmd.getPermission()); + assertTrue(cmd.isOnlyPlayer()); + assertEquals("chat.team-chat.mute.description", cmd.getDescription()); + } + + @Test + public void testCanExecuteNoTeam() { + when(im.inTeam(any(World.class), eq(uuid))).thenReturn(false); + assertFalse(cmd.canExecute(user, "muteteamchat", Collections.emptyList())); + verify(user).sendMessage("general.errors.no-team"); + } + + @Test + public void testCanExecuteInTeam() { + when(im.inTeam(any(World.class), eq(uuid))).thenReturn(true); + assertTrue(cmd.canExecute(user, "muteteamchat", Collections.emptyList())); + verify(user, never()).sendMessage(anyString()); + } + + @Test + public void testExecuteMuteOn() { + when(chatListener.toggleMuteTeamChat(uuid)).thenReturn(true); + assertTrue(cmd.execute(user, "muteteamchat", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.mute.mute-on"); + } + + @Test + public void testExecuteMuteOff() { + when(chatListener.toggleMuteTeamChat(uuid)).thenReturn(false); + assertTrue(cmd.execute(user, "muteteamchat", Collections.emptyList())); + verify(user).sendMessage("chat.team-chat.mute.mute-off"); + } +} diff --git a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java index b270d30..733d2c5 100644 --- a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java +++ b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java @@ -319,4 +319,61 @@ public void testOnKickRemovesTeamChatUser() { assertFalse(listener.isTeamChat(uuid)); } + + // ----------------------------------------------------------------------- + // toggleMuteTeamChat tests + // ----------------------------------------------------------------------- + + @Test + public void testToggleMuteTeamChatOn() { + UUID uid = UUID.randomUUID(); + assertTrue(listener.toggleMuteTeamChat(uid), + "First toggle should mute team chat"); + } + + @Test + public void testToggleMuteTeamChatOff() { + UUID uid = UUID.randomUUID(); + listener.toggleMuteTeamChat(uid); // mute + assertFalse(listener.toggleMuteTeamChat(uid), + "Second toggle should unmute team chat"); + } + + @Test + public void testIsMutedTeamChatFalseByDefault() { + assertFalse(listener.isMutedTeamChat(UUID.randomUUID())); + } + + @Test + public void testIsMutedTeamChatTrueAfterToggle() { + UUID uid = UUID.randomUUID(); + listener.toggleMuteTeamChat(uid); + assertTrue(listener.isMutedTeamChat(uid)); + } + + @Test + public void testOnLeaveRemovesMuteStatus() { + listener.toggleMuteTeamChat(uuid); + assertTrue(listener.isMutedTeamChat(uuid)); + + world.bentobox.bentobox.api.events.team.TeamLeaveEvent leaveEvent = + mock(world.bentobox.bentobox.api.events.team.TeamLeaveEvent.class); + when(leaveEvent.getPlayerUUID()).thenReturn(uuid); + listener.onLeave(leaveEvent); + + assertFalse(listener.isMutedTeamChat(uuid)); + } + + @Test + public void testOnKickRemovesMuteStatus() { + listener.toggleMuteTeamChat(uuid); + assertTrue(listener.isMutedTeamChat(uuid)); + + world.bentobox.bentobox.api.events.team.TeamKickEvent kickEvent = + mock(world.bentobox.bentobox.api.events.team.TeamKickEvent.class); + when(kickEvent.getPlayerUUID()).thenReturn(uuid); + listener.onKick(kickEvent); + + assertFalse(listener.isMutedTeamChat(uuid)); + } } From d5635b29bc31ee3a3b47c838f6518ee79180da87 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:12:05 +0000 Subject: [PATCH 14/36] Add extra-chat-worlds config to enable team chat in non-game worlds Adds a new config option `team-chat.extra-chat-worlds` that maps game mode names to lists of additional world names where team chat should be captured. When a player chats in an extra world, their team chat is resolved through the mapped game mode(s). If multiple game modes cover the same world, chat may go to multiple teams. Settings: new extraChatWorlds Map> field Chat: new getWorldsFromExtra() method to resolve extra worlds to game mode overworlds ChatListener: onChat() now checks extra chat worlds before falling back to default Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- src/main/java/world/bentobox/chat/Chat.java | 25 ++++++++ .../java/world/bentobox/chat/Settings.java | 22 +++++++ .../bentobox/chat/listeners/ChatListener.java | 47 ++++++++++----- src/main/resources/config.yml | 13 +++++ .../world/bentobox/chat/SettingsTest.java | 20 +++++++ .../chat/listeners/ChatListenerTest.java | 57 +++++++++++++++++++ 6 files changed, 169 insertions(+), 15 deletions(-) diff --git a/src/main/java/world/bentobox/chat/Chat.java b/src/main/java/world/bentobox/chat/Chat.java index a03cd87..c2656d2 100644 --- a/src/main/java/world/bentobox/chat/Chat.java +++ b/src/main/java/world/bentobox/chat/Chat.java @@ -1,6 +1,9 @@ package world.bentobox.chat; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -99,6 +102,28 @@ public boolean isRegisteredGameWorld(World world) { return registeredGameModes.parallelStream().anyMatch(gmw -> gmw.inWorld(world)); } + /** + * Get the game mode overworlds for team chat based on extra chat worlds configuration. + * If a world is listed as an extra chat world for one or more game modes, this returns + * the overworlds of those game modes. + * @param worldName - the world name to check + * @return list of game mode overworlds that have this world listed as extra + */ + public List getWorldsFromExtra(String worldName) { + Map> extraWorlds = settings.getExtraChatWorlds(); + List result = new ArrayList<>(); + for (Map.Entry> entry : extraWorlds.entrySet()) { + if (entry.getValue().contains(worldName)) { + getPlugin().getAddonsManager().getGameModeAddons().stream() + .filter(gm -> entry.getKey().equalsIgnoreCase(gm.getDescription().getName())) + .findFirst() + .map(GameModeAddon::getOverWorld) + .ifPresent(result::add); + } + } + return result; + } + /** * Loads the Settings from the config. diff --git a/src/main/java/world/bentobox/chat/Settings.java b/src/main/java/world/bentobox/chat/Settings.java index 7690af0..e5efa84 100644 --- a/src/main/java/world/bentobox/chat/Settings.java +++ b/src/main/java/world/bentobox/chat/Settings.java @@ -1,7 +1,9 @@ package world.bentobox.chat; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.bukkit.event.EventPriority; @@ -27,6 +29,12 @@ public class Settings implements ConfigObject { @ConfigEntry(path = "team-chat.default-teamchat-gamemode") private String defaultChatGamemode = ""; + @ConfigComment("Additional chat worlds. List the additional worlds for each active game mode") + @ConfigComment("where team chat should be captured. If more than one game mode covers a world,") + @ConfigComment("then chat may go to multiple teams.") + @ConfigEntry(path = "team-chat.extra-chat-worlds") + private Map> extraChatWorlds = new HashMap<>(); + @ConfigComment("Log team chats to console.") @ConfigEntry(path = "team-chat.log") private boolean logTeamChats; @@ -121,6 +129,20 @@ public void setDefaultChatGamemode(String universaleChatGamemode) { this.defaultChatGamemode = universaleChatGamemode; } + /** + * @return the extraChatWorlds + */ + public Map> getExtraChatWorlds() { + return extraChatWorlds; + } + + /** + * @param extraChatWorlds the extraChatWorlds to set + */ + public void setExtraChatWorlds(Map> extraChatWorlds) { + this.extraChatWorlds = extraChatWorlds; + } + /** * @param eventPriority the eventPriority to set */ diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 7e3cca0..dddc6dd 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -1,7 +1,9 @@ package world.bentobox.chat.listeners; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -62,26 +64,41 @@ public void execute(Listener listener, Event e) { public void onChat(final AsyncPlayerChatEvent e) { Player p = e.getPlayer(); - World ww = e.getPlayer().getWorld(); - // Check world - if (!addon.isRegisteredGameWorld(ww)) { - // Check to see if there is a default game mode for chat - if (addon.getChatWorld().isPresent()) { - ww = addon.getChatWorld().get(); - } else { + World playerWorld = e.getPlayer().getWorld(); + + // Determine the worlds to use for team chat + List teamChatWorlds = new ArrayList<>(); + if (addon.isRegisteredGameWorld(playerWorld)) { + teamChatWorlds.add(playerWorld); + } else { + // Check extra chat worlds config + teamChatWorlds.addAll(addon.getWorldsFromExtra(playerWorld.getName())); + // If no extra worlds matched, check default chat world + if (teamChatWorlds.isEmpty()) { + addon.getChatWorld().ifPresent(teamChatWorlds::add); + } + // If still empty, nothing to do + if (teamChatWorlds.isEmpty()) { return; } } - World w = ww; - if (teamChatUsers.contains(p.getUniqueId()) && addon.getIslands().inTeam(w, p.getUniqueId())) { - // Cancel the event - e.setCancelled(true); - if (e.isAsynchronous()) { - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> teamChat(w, p, e.getMessage())); - } else { - teamChat(w, p, e.getMessage()); + + // Process team chat for all matching worlds + if (teamChatUsers.contains(p.getUniqueId())) { + for (World w : teamChatWorlds) { + if (addon.getIslands().inTeam(w, p.getUniqueId())) { + // Cancel the event + e.setCancelled(true); + if (e.isAsynchronous()) { + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> teamChat(w, p, e.getMessage())); + } else { + teamChat(w, p, e.getMessage()); + } + } } } + + // Island chat - uses physical location, only meaningful if player is on an island addon.getIslands().getIslandAt(p.getLocation()) .filter(islandChatters.keySet()::contains) .filter(i -> islandChatters.get(i).contains(p)) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c882040..6a52881 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -13,6 +13,19 @@ team-chat: # If players are outside a game world, team chat can still exist for one game mode. List it # here if you want that, e.g., BSkyBlock default-teamchat-gamemode: '' + # Additional chat worlds. List the additional worlds for each active game mode + # where team chat should be captured. If more than one game mode covers a world, + # then chat may go to multiple teams. + # Example: + # extra-chat-worlds: + # BSkyBlock: + # - world + # - world_nether + # - world_the_end + # - spawn_world + # AcidIsland: + # - spawn_world + extra-chat-worlds: {} # Log team chats to console. log: false island-chat: diff --git a/src/test/java/world/bentobox/chat/SettingsTest.java b/src/test/java/world/bentobox/chat/SettingsTest.java index 6c70d74..40d3265 100644 --- a/src/test/java/world/bentobox/chat/SettingsTest.java +++ b/src/test/java/world/bentobox/chat/SettingsTest.java @@ -7,7 +7,9 @@ import static org.mockito.Mockito.mock; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.bukkit.event.EventPriority; import org.junit.jupiter.api.BeforeEach; @@ -139,4 +141,22 @@ public void testSetDefaultChatGamemode() { settings.setDefaultChatGamemode("BSkyBlock"); assertEquals("BSkyBlock", settings.getDefaultChatGamemode()); } + + @Test + public void testDefaultExtraChatWorlds() { + Map> defaults = settings.getExtraChatWorlds(); + assertNotNull(defaults); + assertTrue(defaults.isEmpty()); + } + + @Test + public void testSetExtraChatWorlds() { + Map> extra = new HashMap<>(); + extra.put("BSkyBlock", Arrays.asList("world", "spawn_world")); + extra.put("AcidIsland", Arrays.asList("spawn_world")); + settings.setExtraChatWorlds(extra); + assertEquals(extra, settings.getExtraChatWorlds()); + assertEquals(2, settings.getExtraChatWorlds().get("BSkyBlock").size()); + assertEquals(1, settings.getExtraChatWorlds().get("AcidIsland").size()); + } } diff --git a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java index b270d30..6a8fc70 100644 --- a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java +++ b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.when; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -155,12 +156,68 @@ public void testOnChatNotInRegisteredWorld() { // Not in a registered game world when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(false); when(addon.getChatWorld()).thenReturn(Optional.empty()); + when(addon.getWorldsFromExtra(any(String.class))).thenReturn(Collections.emptyList()); AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello", Collections.emptySet()); listener.onChat(event); assertFalse(event.isCancelled()); } + @Test + public void testOnChatExtraChatWorldTeamChat() { + // Player is NOT in a registered game world + when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(false); + when(addon.getChatWorld()).thenReturn(Optional.empty()); + // But the player's world is an extra chat world for BSkyBlock + World gameWorld = mock(World.class); + when(addon.getWorldsFromExtra("BSkyBlock_world")).thenReturn(List.of(gameWorld)); + + // Player has team chat enabled and is in a team + listener.togglePlayerTeamChat(uuid); + when(im.inTeam(gameWorld, uuid)).thenReturn(true); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello from spawn!", Collections.emptySet()); + listener.onChat(event); + // Chat should be cancelled (intercepted for team chat via extra world) + assertTrue(event.isCancelled()); + } + + @Test + public void testOnChatExtraChatWorldNoTeamChat() { + // Player is NOT in a registered game world + when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(false); + when(addon.getChatWorld()).thenReturn(Optional.empty()); + // The player's world is an extra chat world + World gameWorld = mock(World.class); + when(addon.getWorldsFromExtra("BSkyBlock_world")).thenReturn(List.of(gameWorld)); + + // Player does NOT have team chat enabled + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello!", Collections.emptySet()); + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + listener.onChat(event); + // Not cancelled because team chat is not toggled on + assertFalse(event.isCancelled()); + } + + @Test + public void testOnChatExtraChatWorldFallsBackToDefault() { + // Player is NOT in a registered game world + when(addon.isRegisteredGameWorld(any(World.class))).thenReturn(false); + // No extra chat worlds match + when(addon.getWorldsFromExtra("BSkyBlock_world")).thenReturn(Collections.emptyList()); + // But there is a default chat world + World defaultWorld = mock(World.class); + when(addon.getChatWorld()).thenReturn(Optional.of(defaultWorld)); + + // Player has team chat enabled and is in a team + listener.togglePlayerTeamChat(uuid); + when(im.inTeam(defaultWorld, uuid)).thenReturn(true); + + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(false, player, "Hello default!", Collections.emptySet()); + listener.onChat(event); + assertTrue(event.isCancelled()); + } + @Test public void testOnChatTeamChatActive() { // Player has team chat enabled and is in a team From 844a5ce549133e115e7a0938495598eb1b31cbae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:14:28 +0000 Subject: [PATCH 15/36] Add clarifying comment about multi-game-mode extra world behavior Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- src/main/java/world/bentobox/chat/listeners/ChatListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index dddc6dd..a317230 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -83,7 +83,8 @@ public void onChat(final AsyncPlayerChatEvent e) { } } - // Process team chat for all matching worlds + // Process team chat for all matching worlds. + // If multiple game modes cover the same extra world, chat goes to all matching teams. if (teamChatUsers.contains(p.getUniqueId())) { for (World w : teamChatWorlds) { if (addon.getIslands().inTeam(w, p.getUniqueId())) { From b3fad683dd24bddae3ed1ac78c8db6217ca14cc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:22:20 +0000 Subject: [PATCH 16/36] Initial plan From 0ff561be9b696036b1993b8821cc25362c9edc8a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:29:27 +0000 Subject: [PATCH 17/36] Add missing language translations for all supported locales Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- src/main/resources/locales/cs.yml | 4 ++++ src/main/resources/locales/de.yml | 6 +++++- src/main/resources/locales/es.yml | 4 ++++ src/main/resources/locales/fr.yml | 4 ++++ src/main/resources/locales/hr.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/hu.yml | 4 ++++ src/main/resources/locales/id.yml | 4 ++++ src/main/resources/locales/it.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/ja.yml | 28 ++++++++++++++++------------ src/main/resources/locales/ko.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/lv.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/nl.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/pl.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/pt-BR.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/pt.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/ro.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/ru.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/tr.yml | 4 ++++ src/main/resources/locales/uk.yml | 25 +++++++++++++++++++++++++ src/main/resources/locales/vi.yml | 4 ++++ src/main/resources/locales/zh-CN.yml | 4 ++++ src/main/resources/locales/zh-HK.yml | 25 +++++++++++++++++++++++++ 22 files changed, 353 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/locales/hr.yml create mode 100644 src/main/resources/locales/it.yml create mode 100644 src/main/resources/locales/ko.yml create mode 100644 src/main/resources/locales/lv.yml create mode 100644 src/main/resources/locales/nl.yml create mode 100644 src/main/resources/locales/pl.yml create mode 100644 src/main/resources/locales/pt-BR.yml create mode 100644 src/main/resources/locales/pt.yml create mode 100644 src/main/resources/locales/ro.yml create mode 100644 src/main/resources/locales/ru.yml create mode 100644 src/main/resources/locales/uk.yml create mode 100644 src/main/resources/locales/zh-HK.yml diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml index 60c8b39..74f756b 100644 --- a/src/main/resources/locales/cs.yml +++ b/src/main/resources/locales/cs.yml @@ -6,6 +6,10 @@ chat: chat-on: "Týmový chat zapnut" chat-off: "Týmový chat vypnut" syntax: "&6[team]&a<[name]> &f[message]" + mute: + description: "Přepnout ztlumení zpráv týmového chatu" + mute-on: "&c Týmový chat ztlumen." + mute-off: "&a Týmový chat odztlumen." spy: description: "Přepnout šmírování všech týmových chatů" spy-on: "Šmírování týmových chatů zapnuto" diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index f6144eb..210b01d 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a Team Chat an." chat-off: "&a Team Chat aus." syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "Team Chat Stummschalten ein- oder ausschalten" + mute-on: "&c Team Chat stummgeschaltet." + mute-off: "&a Team Chat Stummschaltung aufgehoben." spy: description: "Team Chat Socialspy ein- oder ausschalten" spy-on: "&a Team Chat Socialspy an." @@ -17,5 +21,5 @@ chat: spy: description: "Insel Chat Socialspy ein- oder ausschalten" spy-on: "&a Insel Chat Socialspy an." - spy-on: "&a Insel Chat Socialspy aus." + spy-off: "&c Insel Chat Socialspy aus." syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml index 81c8193..76e9e60 100644 --- a/src/main/resources/locales/es.yml +++ b/src/main/resources/locales/es.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a Chat de equipo activado" chat-off: "&c Chat de equipo desactivado" syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "Alternar silenciar mensajes del chat de equipo" + mute-on: "&c Chat de equipo silenciado." + mute-off: "&a Chat de equipo sin silencio." spy: description: "Alternar espiar en todos los chats de equipo" spy-on: "&a Chat de equipo espía activado" diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index ae80b42..c365970 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a Chat d'équipe activé." chat-off: "&a Chat d'équipe désactivé." syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "activer/désactiver la mise en sourdine des messages du chat d'équipe" + mute-on: "&c Chat d'équipe mis en sourdine." + mute-off: "&a Chat d'équipe sorti de sourdine." spy: description: "activer/désactiver la surveillance de tous les chats d'équipe" spy-on: "&a Surveillance des chats d'équipe activée." diff --git a/src/main/resources/locales/hr.yml b/src/main/resources/locales/hr.yml new file mode 100644 index 0000000..1a8726c --- /dev/null +++ b/src/main/resources/locales/hr.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "uključuje/isključuje timski chat" + chat-on: "&a Timski chat uključen." + chat-off: "&a Timski chat isključen." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "uključuje/isključuje nijeme poruke timskog chata" + mute-on: "&c Timski chat je utišan." + mute-off: "&a Timski chat više nije utišan." + spy: + description: "uključuje/isključuje praćenje svih timskih chatova" + spy-on: "&a Praćenje timskog chata uključeno." + spy-off: "&c Praćenje timskog chata isključeno." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "uključuje/isključuje chat otoka" + island-on: "&a Chat otoka uključen." + island-off: "&c Chat otoka isključen." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "uključuje/isključuje praćenje svih chatova otoka" + spy-on: "&a Praćenje chata otoka uključeno." + spy-off: "&c Praćenje chata otoka isključeno." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml index 8f37f48..e43b7eb 100644 --- a/src/main/resources/locales/hu.yml +++ b/src/main/resources/locales/hu.yml @@ -4,6 +4,10 @@ chat: chat-on: '&a Csapat chat be.' chat-off: '&a Csapat chat ki.' syntax: '&6[team] &a[name]&8: &f[message]' + mute: + description: csapat chat üzenetek elnémítása be/ki + mute-on: '&c Csapat chat elnémítva.' + mute-off: '&a Csapat chat elnémítás feloldva.' spy: description: kémkedés állítása az összes csapat chatben spy-on: '&a Csapat chat kémkedés be.' diff --git a/src/main/resources/locales/id.yml b/src/main/resources/locales/id.yml index d129fc0..2108af3 100644 --- a/src/main/resources/locales/id.yml +++ b/src/main/resources/locales/id.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a Percakapan tim dinyalakan." chat-off: "&a Percakapan tim dimatikan." syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "nyala/matikan senyap pesan percakapan tim" + mute-on: "&c Percakapan tim dibisukan." + mute-off: "&a Percakapan tim tidak lagi dibisukan." spy: description: "nyala/matikan pengawasan pada semua percakapan tim" spy-on: "&a Pengawasan percakapan tim dinyalakan." diff --git a/src/main/resources/locales/it.yml b/src/main/resources/locales/it.yml new file mode 100644 index 0000000..3c63052 --- /dev/null +++ b/src/main/resources/locales/it.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "attiva/disattiva la chat del team" + chat-on: "&a Chat del team attivata." + chat-off: "&a Chat del team disattivata." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "attiva/disattiva il silenziamento dei messaggi della chat del team" + mute-on: "&c Chat del team silenziata." + mute-off: "&a Chat del team non più silenziata." + spy: + description: "attiva/disattiva la spia su tutte le chat del team" + spy-on: "&a Spia chat del team attivata." + spy-off: "&c Spia chat del team disattivata." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "attiva/disattiva la chat dell'isola" + island-on: "&a Chat dell'isola attivata." + island-off: "&c Chat dell'isola disattivata." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "attiva/disattiva la spia su tutte le chat dell'isola" + spy-on: "&a Spia chat dell'isola attivata." + spy-off: "&c Spia chat dell'isola disattivata." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/ja.yml b/src/main/resources/locales/ja.yml index 0765de8..3058507 100644 --- a/src/main/resources/locales/ja.yml +++ b/src/main/resources/locales/ja.yml @@ -2,21 +2,25 @@ chat: team-chat: description: チームチャットのオン/オフを切り替えます - chat-on: "&aチームチャット。" - chat-off: "&aチームチャットオフ。" + chat-on: "&a チームチャット オン." + chat-off: "&a チームチャット オフ." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: チームチャットメッセージのミュートを切り替えます + mute-on: "&c チームチャットをミュートしました." + mute-off: "&a チームチャットのミュートを解除しました." spy: description: すべてのチームチャットのスパイを切り替えます - spy-on: "&aチームチャットスパイ。" - spy-off: "&cチームチャットスパイオフ。" - syntax: "&c [team-chat-spy]&a [name]&8:[message]" - syntax: "&6 [team]&a [name&8:&f [message]" + spy-on: "&a チームチャットスパイ オン." + spy-off: "&c チームチャットスパイ オフ." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" island-chat: description: アイランドチャットのオン/オフを切り替えます + island-on: "&a アイランドチャット オン." + island-off: "&c アイランドチャット オフ." + syntax: "&6[island] &a[name]&8: &f[message]" spy: description: すべての島のチャットのスパイを切り替えます - spy-on: "&a島のチャットスパイ。" - syntax: "&c [island-chat-spy]&a [name]&8:&f [message]" - spy-off: "&c島のチャットスパイオフ。" - island-on: "&a島のチャット。" - island-off: "&c島のチャットオフ。" - syntax: "&6 [island]&a [name]&8:&f [message]" + spy-on: "&a アイランドチャットスパイ オン." + spy-off: "&c アイランドチャットスパイ オフ." + syntax: "&c[island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml new file mode 100644 index 0000000..97d3e2e --- /dev/null +++ b/src/main/resources/locales/ko.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "팀 채팅 켜기/끄기 전환" + chat-on: "&a 팀 채팅이 켜졌습니다." + chat-off: "&a 팀 채팅이 꺼졌습니다." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "팀 채팅 메시지 음소거 전환" + mute-on: "&c 팀 채팅이 음소거되었습니다." + mute-off: "&a 팀 채팅 음소거가 해제되었습니다." + spy: + description: "모든 팀 채팅 감시 켜기/끄기 전환" + spy-on: "&a 팀 채팅 감시가 켜졌습니다." + spy-off: "&c 팀 채팅 감시가 꺼졌습니다." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "섬 채팅 켜기/끄기 전환" + island-on: "&a 섬 채팅이 켜졌습니다." + island-off: "&c 섬 채팅이 꺼졌습니다." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "모든 섬 채팅 감시 켜기/끄기 전환" + spy-on: "&a 섬 채팅 감시가 켜졌습니다." + spy-off: "&c 섬 채팅 감시가 꺼졌습니다." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml new file mode 100644 index 0000000..d5d1b3b --- /dev/null +++ b/src/main/resources/locales/lv.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "ieslēdz/izslēdz komandas tērzēšanu" + chat-on: "&a Komandas tērzēšana ieslēgta." + chat-off: "&a Komandas tērzēšana izslēgta." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "ieslēdz/izslēdz komandas tērzēšanas ziņojumu apklusināšanu" + mute-on: "&c Komandas tērzēšana apklusināta." + mute-off: "&a Komandas tērzēšanas apklusināšana atcelta." + spy: + description: "ieslēdz/izslēdz visas komandas tērzēšanas uzraudzību" + spy-on: "&a Komandas tērzēšanas uzraudzība ieslēgta." + spy-off: "&c Komandas tērzēšanas uzraudzība izslēgta." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "ieslēdz/izslēdz salas tērzēšanu" + island-on: "&a Salas tērzēšana ieslēgta." + island-off: "&c Salas tērzēšana izslēgta." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "ieslēdz/izslēdz visu salu tērzēšanas uzraudzību" + spy-on: "&a Salas tērzēšanas uzraudzība ieslēgta." + spy-off: "&c Salas tērzēšanas uzraudzība izslēgta." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/nl.yml b/src/main/resources/locales/nl.yml new file mode 100644 index 0000000..57c9809 --- /dev/null +++ b/src/main/resources/locales/nl.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "zet teamchat aan/uit" + chat-on: "&a Teamchat aan." + chat-off: "&a Teamchat uit." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "zet het dempen van teamchat berichten aan/uit" + mute-on: "&c Teamchat gedempt." + mute-off: "&a Teamchat niet meer gedempt." + spy: + description: "zet het bespioneren van alle teamchats aan/uit" + spy-on: "&a Teamchat spion aan." + spy-off: "&c Teamchat spion uit." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "zet eilandchat aan/uit" + island-on: "&a Eilandchat aan." + island-off: "&c Eilandchat uit." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "zet het bespioneren van alle eilandchats aan/uit" + spy-on: "&a Eilandchat spion aan." + spy-off: "&c Eilandchat spion uit." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml new file mode 100644 index 0000000..b6ab094 --- /dev/null +++ b/src/main/resources/locales/pl.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "włącza/wyłącza czat drużynowy" + chat-on: "&a Czat drużynowy włączony." + chat-off: "&a Czat drużynowy wyłączony." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "włącza/wyłącza wyciszanie wiadomości czatu drużynowego" + mute-on: "&c Czat drużynowy wyciszony." + mute-off: "&a Czat drużynowy odciszony." + spy: + description: "włącza/wyłącza szpiegowanie wszystkich czatów drużynowych" + spy-on: "&a Szpiegowanie czatu drużynowego włączone." + spy-off: "&c Szpiegowanie czatu drużynowego wyłączone." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "włącza/wyłącza czat wyspy" + island-on: "&a Czat wyspy włączony." + island-off: "&c Czat wyspy wyłączony." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "włącza/wyłącza szpiegowanie wszystkich czatów wyspy" + spy-on: "&a Szpiegowanie czatu wyspy włączone." + spy-off: "&c Szpiegowanie czatu wyspy wyłączone." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/pt-BR.yml b/src/main/resources/locales/pt-BR.yml new file mode 100644 index 0000000..cc6add7 --- /dev/null +++ b/src/main/resources/locales/pt-BR.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "ativa/desativa o chat da equipe" + chat-on: "&a Chat da equipe ativado." + chat-off: "&a Chat da equipe desativado." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "ativa/desativa o silenciamento de mensagens do chat da equipe" + mute-on: "&c Chat da equipe silenciado." + mute-off: "&a Chat da equipe não está mais silenciado." + spy: + description: "ativa/desativa a espionagem de todos os chats da equipe" + spy-on: "&a Espião de chat da equipe ativado." + spy-off: "&c Espião de chat da equipe desativado." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "ativa/desativa o chat da ilha" + island-on: "&a Chat da ilha ativado." + island-off: "&c Chat da ilha desativado." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "ativa/desativa a espionagem de todos os chats da ilha" + spy-on: "&a Espião de chat da ilha ativado." + spy-off: "&c Espião de chat da ilha desativado." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/pt.yml b/src/main/resources/locales/pt.yml new file mode 100644 index 0000000..12d8d94 --- /dev/null +++ b/src/main/resources/locales/pt.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "ativa/desativa o chat da equipa" + chat-on: "&a Chat da equipa ativado." + chat-off: "&a Chat da equipa desativado." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "ativa/desativa o silenciamento de mensagens do chat da equipa" + mute-on: "&c Chat da equipa silenciado." + mute-off: "&a Chat da equipa não está mais silenciado." + spy: + description: "ativa/desativa a espionagem de todos os chats da equipa" + spy-on: "&a Espião de chat da equipa ativado." + spy-off: "&c Espião de chat da equipa desativado." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "ativa/desativa o chat da ilha" + island-on: "&a Chat da ilha ativado." + island-off: "&c Chat da ilha desativado." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "ativa/desativa a espionagem de todos os chats da ilha" + spy-on: "&a Espião de chat da ilha ativado." + spy-off: "&c Espião de chat da ilha desativado." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml new file mode 100644 index 0000000..26b1434 --- /dev/null +++ b/src/main/resources/locales/ro.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "activează/dezactivează chat-ul de echipă" + chat-on: "&a Chat de echipă activat." + chat-off: "&a Chat de echipă dezactivat." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "activează/dezactivează dezactivarea sunetului chat-ului de echipă" + mute-on: "&c Chat de echipă dezactivat sonor." + mute-off: "&a Chat de echipă reactivat sonor." + spy: + description: "activează/dezactivează spionarea tuturor chat-urilor de echipă" + spy-on: "&a Spion chat de echipă activat." + spy-off: "&c Spion chat de echipă dezactivat." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "activează/dezactivează chat-ul de insulă" + island-on: "&a Chat de insulă activat." + island-off: "&c Chat de insulă dezactivat." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "activează/dezactivează spionarea tuturor chat-urilor de insulă" + spy-on: "&a Spion chat de insulă activat." + spy-off: "&c Spion chat de insulă dezactivat." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml new file mode 100644 index 0000000..65802fd --- /dev/null +++ b/src/main/resources/locales/ru.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "включает/выключает командный чат" + chat-on: "&a Командный чат включён." + chat-off: "&a Командный чат выключен." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "включает/выключает отключение звука командного чата" + mute-on: "&c Командный чат заглушен." + mute-off: "&a Командный чат разглушен." + spy: + description: "включает/выключает слежку за всеми командными чатами" + spy-on: "&a Слежка за командным чатом включена." + spy-off: "&c Слежка за командным чатом выключена." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "включает/выключает чат острова" + island-on: "&a Чат острова включён." + island-off: "&c Чат острова выключен." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "включает/выключает слежку за всеми чатами острова" + spy-on: "&a Слежка за чатом острова включена." + spy-off: "&c Слежка за чатом острова выключена." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/tr.yml b/src/main/resources/locales/tr.yml index 35579a1..22a35bd 100644 --- a/src/main/resources/locales/tr.yml +++ b/src/main/resources/locales/tr.yml @@ -8,6 +8,10 @@ chat: chat-on: "&aTakım sohbeti aktifleştirildi." chat-off: "&cTakım sohbeti kapatıldı." syntax: "&6[team] &a[name] &8: &f[message]" + mute: + description: "takım sohbeti mesajlarını susturmayı açar/kapatır" + mute-on: "&cTakım sohbeti susturuldu." + mute-off: "&aTakım sohbeti susturma kaldırıldı." spy: description: "takım sohbetlerini görmeni sağlar" spy-on: "&aTakım sohbetlerini artık görebiliyorsun." diff --git a/src/main/resources/locales/uk.yml b/src/main/resources/locales/uk.yml new file mode 100644 index 0000000..b307cd3 --- /dev/null +++ b/src/main/resources/locales/uk.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "вмикає/вимикає командний чат" + chat-on: "&a Командний чат увімкнено." + chat-off: "&a Командний чат вимкнено." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "вмикає/вимикає ігнорування повідомлень командного чату" + mute-on: "&c Командний чат заглушено." + mute-off: "&a Командний чат розглушено." + spy: + description: "вмикає/вимикає стеження за всіма командними чатами" + spy-on: "&a Стеження за командним чатом увімкнено." + spy-off: "&c Стеження за командним чатом вимкнено." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "вмикає/вимикає чат острова" + island-on: "&a Чат острова увімкнено." + island-off: "&c Чат острова вимкнено." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "вмикає/вимикає стеження за всіма чатами острова" + spy-on: "&a Стеження за чатом острова увімкнено." + spy-off: "&c Стеження за чатом острова вимкнено." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" diff --git a/src/main/resources/locales/vi.yml b/src/main/resources/locales/vi.yml index ea8773a..2d6595c 100644 --- a/src/main/resources/locales/vi.yml +++ b/src/main/resources/locales/vi.yml @@ -4,6 +4,10 @@ chat: chat-on: '&aĐã bật chat đội.' chat-off: '&aĐã tắt chat đội.' syntax: '&6[team] &a[name]&8: &f[message]' + mute: + description: bật/tắt tắt tiếng tin nhắn chat đội + mute-on: '&cĐã tắt tiếng chat đội.' + mute-off: '&aĐã bỏ tắt tiếng chat đội.' spy: description: bật/tắt giám sát chat đội spy-on: '&aĐã bật giám sát chat đội.' diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 6e9c741..effed47 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -4,6 +4,10 @@ chat: chat-on: "&a 团队聊天已开启." chat-off: "&a 团队聊天已关闭." syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "切换静音团队聊天消息" + mute-on: "&c 团队聊天已静音." + mute-off: "&a 团队聊天已取消静音." spy: description: "切换偷听所有团队聊天" spy-on: "&a 团队聊天偷听已开启." diff --git a/src/main/resources/locales/zh-HK.yml b/src/main/resources/locales/zh-HK.yml new file mode 100644 index 0000000..31b30e0 --- /dev/null +++ b/src/main/resources/locales/zh-HK.yml @@ -0,0 +1,25 @@ +chat: + team-chat: + description: "切換團隊聊天開關" + chat-on: "&a 團隊聊天已開啟." + chat-off: "&a 團隊聊天已關閉." + syntax: "&6[team] &a[name]&8: &f[message]" + mute: + description: "切換靜音團隊聊天訊息" + mute-on: "&c 團隊聊天已靜音." + mute-off: "&a 團隊聊天已取消靜音." + spy: + description: "切換監視所有團隊聊天" + spy-on: "&a 團隊聊天監視已開啟." + spy-off: "&c 團隊聊天監視已關閉." + syntax: "&c[team-chat-spy] &a[name]&8: [message]" + island-chat: + description: "切換島嶼聊天開關" + island-on: "&a 島嶼聊天已開啟." + island-off: "&c 島嶼聊天已關閉." + syntax: "&6[island] &a[name]&8: &f[message]" + spy: + description: "切換監視所有島嶼聊天" + spy-on: "&a 島嶼聊天監視已開啟." + spy-off: "&c 島嶼聊天監視已關閉." + syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" From 493cb580c2c0aa6730352fce8cdc3756f6b7aa64 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 22:55:17 -0700 Subject: [PATCH 18/36] Update to BentoBox 3.14.0-SNAPSHOT, compiler 3.15.0, JaCoCo 0.8.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump bentobox.version 3.10.0 → 3.14.0-SNAPSHOT - Add codemc-repo and codemc snapshots repositories needed for BentoBox snapshots - Upgrade maven-compiler-plugin 3.7.0 → 3.15.0, add true - Upgrade jacoco-maven-plugin 0.8.10 → 0.8.12 - Remove ${argLine} from surefire to prevent JaCoCo agent from instrumenting Mockito-generated Java 25 class files (unsupported by JaCoCo ASM) - Add surefire block and clean up argLine whitespace - Add org/mockito/codegen/* to JaCoCo excludes Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 55 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 8d8f568..d3279d8 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ v1.21-SNAPSHOT 1.21.11-R0.1-SNAPSHOT - 3.10.0 + 3.14.0-SNAPSHOT ${build.version}-SNAPSHOT @@ -131,6 +131,19 @@ true false + + + codemc-repo + https://repo.codemc.org/repository/maven-public/ + true + true + + + codemc + https://repo.codemc.org/repository/maven-snapshots/ + false + true + papermc @@ -226,11 +239,12 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.15.0 ${java.version} ${java.version} ${java.version} + true @@ -238,39 +252,33 @@ maven-surefire-plugin 3.5.2 + + **/*Test.java + **/*Test?.java + **/*Test??.java + - ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED - --add-opens - java.base/java.util.stream=ALL-UNNAMED + --add-opens java.base/java.util.stream=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED - --add-opens - java.base/java.util.regex=ALL-UNNAMED - --add-opens - java.base/java.nio.channels.spi=ALL-UNNAMED + --add-opens java.base/java.util.regex=ALL-UNNAMED + --add-opens java.base/java.nio.channels.spi=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED - --add-opens - java.base/java.util.concurrent=ALL-UNNAMED + --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/sun.nio.fs=ALL-UNNAMED --add-opens java.base/sun.nio.cs=ALL-UNNAMED --add-opens java.base/java.nio.file=ALL-UNNAMED - --add-opens - java.base/java.nio.charset=ALL-UNNAMED - --add-opens - java.base/java.lang.reflect=ALL-UNNAMED - --add-opens - java.logging/java.util.logging=ALL-UNNAMED + --add-opens java.base/java.nio.charset=ALL-UNNAMED + --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.logging/java.util.logging=ALL-UNNAMED --add-opens java.base/java.lang.ref=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.zip=ALL-UNNAMED - --add-opens java.base/java.security=ALL-UNNAMED - --add-opens java.base/jdk.internal.misc=ALL-UNNAMED - @@ -323,15 +331,18 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.12 true - **/*Names* org/bukkit/Material* + + org/mockito/codegen/* From a7b305244083ba193c41693bcb401e9f8e8d93de Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 22:58:11 -0700 Subject: [PATCH 19/36] Update CLAUDE.md to reflect current project state - Paper only (Spigot removed), target Paper 1.21.11 - Add IslandTeamMuteCommand (muteteamchat) to commands list - Update BentoBox dependency to 3.14.0-SNAPSHOT - Document JaCoCo/Java 25 workaround (no ${argLine} in surefire) Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bad86ab..44df2a2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Chat is a BentoBox addon for Minecraft (Paper/Spigot) that provides Team Chat and Island Chat for island-type game modes (BSkyBlock, AcidIsland, CaveBlock, SkyGrid). It requires Java 21 and targets Paper 1.21.x. +Chat is a BentoBox addon for Minecraft (Paper) that provides Team Chat and Island Chat for island-type game modes (BSkyBlock, AcidIsland, CaveBlock, SkyGrid). It requires Java 21 and targets Paper 1.21.11. ## Build Commands @@ -28,7 +28,7 @@ This is a **BentoBox Pladdon** (plugin-addon): - `IsTeamChatHandler` — BentoBox request handler that lets other addons query if a player has team chat enabled Commands follow BentoBox's `CompositeCommand` pattern and are registered dynamically onto each game mode's player/admin command trees: -- Player: `IslandChatCommand` (chat), `IslandTeamChatCommand` (teamchat) +- Player: `IslandChatCommand` (chat), `IslandTeamChatCommand` (teamchat), `IslandTeamMuteCommand` (muteteamchat) - Admin: `AdminIslandChatSpyCommand` (chatspy), `AdminTeamChatSpyCommand` (teamchatspy) ## Testing @@ -36,3 +36,96 @@ Commands follow BentoBox's `CompositeCommand` pattern and are registered dynamic Tests use JUnit 5 + Mockito + MockBukkit. The `CommonTestSetup` base class provides standard mocks for BentoBox, Bukkit, Player, World, Island, etc. New test classes should extend `CommonTestSetup` and call `super.setUp()`/`super.tearDown()`. The `WhiteBox` utility sets private/static fields via reflection for test setup. Localization strings are defined in `src/main/resources/locales/` (en-US.yml is the primary locale). + +### JaCoCo / Java 25 note + +The surefire `` intentionally does **not** include `${argLine}`. When running on Java 25, Mockito generates dynamic class files at class file version 69 (Java 25), which JaCoCo 0.8.12's ASM cannot instrument. Omitting `${argLine}` prevents the JaCoCo agent from being injected into the test JVM, avoiding the `Unsupported class file major version 69` error. JaCoCo still produces a coverage report from the compiled class files. + +## Dependency Source Lookup + +When you need to inspect source code for a dependency (e.g., BentoBox, addons): + +1. **Check local Maven repo first**: `~/.m2/repository/` — sources jars are named `*-sources.jar` +2. **Check the workspace**: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g., `../bentoBox`, `../addon-*`) +3. **Check Maven local cache for already-extracted sources** before downloading anything +4. Only download a jar or fetch from the internet if the above steps yield nothing useful + +Prefer reading `.java` source files directly from a local Git clone over decompiling or extracting a jar. + +In general, the latest version of BentoBox should be targeted. + +## Project Layout + +Related projects are checked out as siblings under `~/git/`: + +**Core:** +- `bentobox/` — core BentoBox framework + +**Game modes:** +- `addon-acidisland/` — AcidIsland game mode +- `addon-bskyblock/` — BSkyBlock game mode +- `Boxed/` — Boxed game mode (expandable box area) +- `CaveBlock/` — CaveBlock game mode +- `OneBlock/` — AOneBlock game mode +- `SkyGrid/` — SkyGrid game mode +- `RaftMode/` — Raft survival game mode +- `StrangerRealms/` — StrangerRealms game mode +- `Brix/` — plot game mode +- `parkour/` — Parkour game mode +- `poseidon/` — Poseidon game mode +- `gg/` — gg game mode + +**Addons:** +- `addon-level/` — island level calculation +- `addon-challenges/` — challenges system +- `addon-welcomewarpsigns/` — warp signs +- `addon-limits/` — block/entity limits +- `addon-invSwitcher/` / `invSwitcher/` — inventory switcher +- `addon-biomes/` / `Biomes/` — biomes management +- `Bank/` — island bank +- `Border/` — world border for islands +- `Chat/` — island chat +- `CheckMeOut/` — island submission/voting +- `ControlPanel/` — game mode control panel +- `Converter/` — ASkyBlock to BSkyBlock converter +- `DimensionalTrees/` — dimension-specific trees +- `discordwebhook/` — Discord integration +- `Downloads/` — BentoBox downloads site +- `DragonFights/` — per-island ender dragon fights +- `ExtraMobs/` — additional mob spawning rules +- `FarmersDance/` — twerking crop growth +- `GravityFlux/` — gravity addon +- `Greenhouses-addon/` — greenhouse biomes +- `IslandFly/` — island flight permission +- `IslandRankup/` — island rankup system +- `Likes/` — island likes/dislikes +- `Limits/` — block/entity limits +- `lost-sheep/` — lost sheep adventure +- `MagicCobblestoneGenerator/` — custom cobblestone generator +- `PortalStart/` — portal-based island start +- `pp/` — pp addon +- `Regionerator/` — region management +- `Residence/` — residence addon +- `TopBlock/` — top ten for OneBlock +- `TwerkingForTrees/` — twerking tree growth +- `Upgrades/` — island upgrades (Vault) +- `Visit/` — island visiting +- `weblink/` — web link addon +- `CrowdBound/` — CrowdBound addon + +**Data packs:** +- `BoxedDataPack/` — advancement datapack for Boxed + +**Documentation & tools:** +- `docs/` — main documentation site +- `docs-chinese/` — Chinese documentation +- `docs-french/` — French documentation +- `BentoBoxWorld.github.io/` — GitHub Pages site +- `website/` — website +- `translation-tool/` — translation tool + +Check these for source before any network fetch. + +## Key Dependencies (source locations) + +- `world.bentobox:bentobox` `3.14.0-SNAPSHOT` → `~/git/bentobox/src/` From 0bd8740e49c5d76da88131dfecf6f588ea807311 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:10:54 -0700 Subject: [PATCH 20/36] Run tests under Java 21 to restore JaCoCo coverage When tests run under Java 25, Mockito generates mock classes at class file version 69 which JaCoCo 0.8.12 cannot instrument. Pinning the surefire JVM to Java 21 via the test.jvm property keeps generated bytecode at version 65 and restores coverage data collection. Also switches surefire argLine back to @{argLine} (late binding) so the JaCoCo agent is properly injected. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 2 +- pom.xml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 44df2a2..f63731d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Localization strings are defined in `src/main/resources/locales/` (en-US.yml is ### JaCoCo / Java 25 note -The surefire `` intentionally does **not** include `${argLine}`. When running on Java 25, Mockito generates dynamic class files at class file version 69 (Java 25), which JaCoCo 0.8.12's ASM cannot instrument. Omitting `${argLine}` prevents the JaCoCo agent from being injected into the test JVM, avoiding the `Unsupported class file major version 69` error. JaCoCo still produces a coverage report from the compiled class files. +Surefire is configured with `${test.jvm}` pointing to Java 21 (`openjdk@21`). This is required because when tests run under Java 25, Mockito generates dynamic mock classes at class file version 69 (Java 25), which JaCoCo 0.8.12's ASM cannot instrument, causing `Unsupported class file major version 69` errors. Running tests under Java 21 keeps Mockito's generated bytecode at version 65, which JaCoCo handles correctly. Coverage data is collected into `target/jacoco.exec` and the report is produced during `mvn verify`. ## Dependency Source Lookup diff --git a/pom.xml b/pom.xml index d3279d8..64699fd 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,8 @@ 5.10.2 5.11.0 v1.21-SNAPSHOT + + /opt/homebrew/Cellar/openjdk@21/21.0.9/libexec/openjdk.jdk/Contents/Home/bin/java 1.21.11-R0.1-SNAPSHOT 3.14.0-SNAPSHOT @@ -252,12 +254,13 @@ maven-surefire-plugin 3.5.2 + ${test.jvm} **/*Test.java **/*Test?.java **/*Test??.java - + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED From e79b73439946f99291bb223c2c30df6bf824d485 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:15:40 -0700 Subject: [PATCH 21/36] Revert JaCoCo workarounds; document Java 25 limitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Option A (agent excludes) and Option B (hardcoded jvm path) both had problems. The root cause is JaCoCo 0.8.12 / ASM 9.6 not supporting class file versions 68/69 (Java 24/25), which affects ByteBuddy's own library classes and JDK tool classes — not just generated mocks. CI uses Java 21 and is unaffected. Locally, run with Java 21 via: JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 6 +++++- pom.xml | 6 ------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f63731d..cbcc541 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,11 @@ Localization strings are defined in `src/main/resources/locales/` (en-US.yml is ### JaCoCo / Java 25 note -Surefire is configured with `${test.jvm}` pointing to Java 21 (`openjdk@21`). This is required because when tests run under Java 25, Mockito generates dynamic mock classes at class file version 69 (Java 25), which JaCoCo 0.8.12's ASM cannot instrument, causing `Unsupported class file major version 69` errors. Running tests under Java 21 keeps Mockito's generated bytecode at version 65, which JaCoCo handles correctly. Coverage data is collected into `target/jacoco.exec` and the report is produced during `mvn verify`. +JaCoCo 0.8.12 uses ASM 9.6, which does not understand class file versions 68 or 69 (Java 24/25). When tests run under Java 25, ByteBuddy and JDK tool classes compiled at those versions are loaded at runtime and cause `Unsupported class file major version 69` errors. CI runs Java 21 and is unaffected. To get coverage locally when your default JVM is Java 25, run: + +```bash +JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test +``` ## Dependency Source Lookup diff --git a/pom.xml b/pom.xml index 64699fd..b288461 100644 --- a/pom.xml +++ b/pom.xml @@ -63,8 +63,6 @@ 5.10.2 5.11.0 v1.21-SNAPSHOT - - /opt/homebrew/Cellar/openjdk@21/21.0.9/libexec/openjdk.jdk/Contents/Home/bin/java 1.21.11-R0.1-SNAPSHOT 3.14.0-SNAPSHOT @@ -254,7 +252,6 @@ maven-surefire-plugin 3.5.2 - ${test.jvm} **/*Test.java **/*Test?.java @@ -343,9 +340,6 @@ **/*Names* org/bukkit/Material* - - org/mockito/codegen/* From b4627f99d60c7b37f20a3e8669aac1345cd2f156 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:17:58 -0700 Subject: [PATCH 22/36] Upgrade JaCoCo to 0.8.14 for Java 25 support 0.8.14 ships ASM with Java 24/25 class file support, fixing the Unsupported class file major version 68/69 errors seen when running tests under Java 25. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 7 ------- pom.xml | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cbcc541..f3063dd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,13 +37,6 @@ Tests use JUnit 5 + Mockito + MockBukkit. The `CommonTestSetup` base class provi Localization strings are defined in `src/main/resources/locales/` (en-US.yml is the primary locale). -### JaCoCo / Java 25 note - -JaCoCo 0.8.12 uses ASM 9.6, which does not understand class file versions 68 or 69 (Java 24/25). When tests run under Java 25, ByteBuddy and JDK tool classes compiled at those versions are loaded at runtime and cause `Unsupported class file major version 69` errors. CI runs Java 21 and is unaffected. To get coverage locally when your default JVM is Java 25, run: - -```bash -JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test -``` ## Dependency Source Lookup diff --git a/pom.xml b/pom.xml index b288461..f318239 100644 --- a/pom.xml +++ b/pom.xml @@ -331,7 +331,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.14 true From 460141d748e17ce9cbc86006440399e2c45a8f71 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:19:38 -0700 Subject: [PATCH 23/36] Update Maven plugin versions to latest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit maven-clean-plugin: 3.1.0 → 3.5.0 maven-resources-plugin: 3.1.0 → 3.5.0 maven-surefire-plugin: 3.5.2 → 3.5.5 maven-jar-plugin: 3.2.0 → 3.5.0 maven-javadoc-plugin: 3.1.1 → 3.12.0 maven-source-plugin: 3.0.1 → 3.4.0 maven-install-plugin: 2.5.2 → 3.1.4 maven-deploy-plugin: 2.8.2 → 3.1.4 Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index f318239..0aba550 100644 --- a/pom.xml +++ b/pom.xml @@ -229,12 +229,12 @@ org.apache.maven.plugins maven-clean-plugin - 3.1.0 + 3.5.0 org.apache.maven.plugins maven-resources-plugin - 3.1.0 + 3.5.0 org.apache.maven.plugins @@ -250,7 +250,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.5 **/*Test.java @@ -284,12 +284,12 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.5.0 org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.12.0 public false @@ -308,7 +308,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.4.0 attach-sources @@ -321,12 +321,12 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.1.4 org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.1.4 org.jacoco From 2858c537abef39e996a33437a8f64815da7437a5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:33:38 -0700 Subject: [PATCH 24/36] Convert all locale files from Bukkit color codes to MiniMessage format - Replace &a/&c/&6/&8/&f/&4 codes with ///// tags - Fix team-chat.chat-off which incorrectly used green in most locales; now consistently red - Remove leading spaces that were artifacts of inline color codes - Fix cs.yml: add missing color tags and remove <> around [name] in syntax (MiniMessage conflict) - Fix es.yml island spy syntax: &4 name color preserved as - Fix ru.yml: add missing mute section that was absent from the partial manual conversion - All 23 locales now use MiniMessage; no &-codes remain Co-Authored-By: Claude Sonnet 4.6 --- src/main/resources/locales/cs.yml | 31 ++++++++++---------- src/main/resources/locales/de.yml | 30 +++++++++---------- src/main/resources/locales/en-US.yml | 30 +++++++++---------- src/main/resources/locales/es.yml | 30 +++++++++---------- src/main/resources/locales/fr.yml | 30 +++++++++---------- src/main/resources/locales/hr.yml | 28 +++++++++--------- src/main/resources/locales/hu.yml | 28 +++++++++--------- src/main/resources/locales/id.yml | 30 +++++++++---------- src/main/resources/locales/it.yml | 28 +++++++++--------- src/main/resources/locales/ja.yml | 28 +++++++++--------- src/main/resources/locales/ko.yml | 28 +++++++++--------- src/main/resources/locales/lv.yml | 28 +++++++++--------- src/main/resources/locales/nl.yml | 28 +++++++++--------- src/main/resources/locales/pl.yml | 28 +++++++++--------- src/main/resources/locales/pt-BR.yml | 28 +++++++++--------- src/main/resources/locales/pt.yml | 28 +++++++++--------- src/main/resources/locales/ro.yml | 28 +++++++++--------- src/main/resources/locales/ru.yml | 43 ++++++++++++++++------------ src/main/resources/locales/tr.yml | 28 +++++++++--------- src/main/resources/locales/uk.yml | 28 +++++++++--------- src/main/resources/locales/vi.yml | 28 +++++++++--------- src/main/resources/locales/zh-CN.yml | 30 +++++++++---------- src/main/resources/locales/zh-HK.yml | 28 +++++++++--------- 23 files changed, 339 insertions(+), 335 deletions(-) diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml index 74f756b..4c79c28 100644 --- a/src/main/resources/locales/cs.yml +++ b/src/main/resources/locales/cs.yml @@ -3,26 +3,25 @@ chat: team-chat: description: "Přepnout týmový chat zapnuto/vypnuto" - chat-on: "Týmový chat zapnut" - chat-off: "Týmový chat vypnut" - syntax: "&6[team]&a<[name]> &f[message]" + chat-on: Týmový chat zapnut + chat-off: Týmový chat vypnut + syntax: [team][name] [message] mute: description: "Přepnout ztlumení zpráv týmového chatu" - mute-on: "&c Týmový chat ztlumen." - mute-off: "&a Týmový chat odztlumen." - spy: + mute-on: Týmový chat ztlumen. + mute-off: Týmový chat odztlumen. + spy: description: "Přepnout šmírování všech týmových chatů" - spy-on: "Šmírování týmových chatů zapnuto" - spy-off: "Šmírování týmových chatů vypnuto" - syntax: "&c[team-chat-spy] &4[name]: [message]" + spy-on: Šmírování týmových chatů zapnuto + spy-off: Šmírování týmových chatů vypnuto + syntax: [team-chat-spy] [name]: [message] island-chat: description: "Přepnout chat na ostrově zapnuto/vypnuto" - island-on: "Chat na ostrově zapnut" - island-off: "Chat na ostrově vypnut" - syntax: "&6[island]&a<[name]> &f[message]" + island-on: Chat na ostrově zapnut + island-off: Chat na ostrově vypnut + syntax: [island][name] [message] spy: description: "Přepnout šmírování chatů na všech ostrovech" - spy-on: "Šmírování chatů na ostrovech zapnuto" - spy-off: "Šmírování chatů na ostrovech vypnuto" - syntax: "&c[island-chat-spy] &4[name]: [message]" - \ No newline at end of file + spy-on: Šmírování chatů na ostrovech zapnuto + spy-off: Šmírování chatů na ostrovech vypnuto + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index 210b01d..dadd72d 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "Team Chat ein- oder ausschalten" - chat-on: "&a Team Chat an." - chat-off: "&a Team Chat aus." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Team Chat an. + chat-off: Team Chat aus. + syntax: [team] [name]: [message] mute: description: "Team Chat Stummschalten ein- oder ausschalten" - mute-on: "&c Team Chat stummgeschaltet." - mute-off: "&a Team Chat Stummschaltung aufgehoben." - spy: + mute-on: Team Chat stummgeschaltet. + mute-off: Team Chat Stummschaltung aufgehoben. + spy: description: "Team Chat Socialspy ein- oder ausschalten" - spy-on: "&a Team Chat Socialspy an." - spy-off: "&c Team Chat Socialspy aus." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Team Chat Socialspy an. + spy-off: Team Chat Socialspy aus. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "Insel Chat ein- oder ausschalten" - island-on: "&a Insel Chat an." - island-off: "&c Insel Chat aus." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Insel Chat an. + island-off: Insel Chat aus. + syntax: [island] [name]: [message] spy: description: "Insel Chat Socialspy ein- oder ausschalten" - spy-on: "&a Insel Chat Socialspy an." - spy-off: "&c Insel Chat Socialspy aus." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Insel Chat Socialspy an. + spy-off: Insel Chat Socialspy aus. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 1b9504a..a17e0f4 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "toggles team chat on/off" - chat-on: "&a Team chat on." - chat-off: "&a Team chat off." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Team chat on. + chat-off: Team chat off. + syntax: [team] [name]: [message] mute: description: "toggles muting team chat messages" - mute-on: "&c Team chat muted." - mute-off: "&a Team chat unmuted." - spy: + mute-on: Team chat muted. + mute-off: Team chat unmuted. + spy: description: "toggles spying on all team chats" - spy-on: "&a Team chat spy on." - spy-off: "&c Team chat spy off." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Team chat spy on. + spy-off: Team chat spy off. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "toggles island chat on/off" - island-on: "&a Island chat on." - island-off: "&c Island chat off." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Island chat on. + island-off: Island chat off. + syntax: [island] [name]: [message] spy: description: "toggles spying on all island chats" - spy-on: "&a Island chat spy on." - spy-off: "&c Island chat spy off." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Island chat spy on. + spy-off: Island chat spy off. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml index 76e9e60..7c7ee86 100644 --- a/src/main/resources/locales/es.yml +++ b/src/main/resources/locales/es.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "Alternar el chat de equipo" - chat-on: "&a Chat de equipo activado" - chat-off: "&c Chat de equipo desactivado" - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Chat de equipo activado + chat-off: Chat de equipo desactivado + syntax: [team] [name]: [message] mute: description: "Alternar silenciar mensajes del chat de equipo" - mute-on: "&c Chat de equipo silenciado." - mute-off: "&a Chat de equipo sin silencio." - spy: + mute-on: Chat de equipo silenciado. + mute-off: Chat de equipo sin silencio. + spy: description: "Alternar espiar en todos los chats de equipo" - spy-on: "&a Chat de equipo espía activado" - spy-off: "&c Chat de equipo espía desactivado" - syntax: "&c[team-chat-spy] &a[name]&8: &f[message]" + spy-on: Chat de equipo espía activado + spy-off: Chat de equipo espía desactivado + syntax: [team-chat-spy] [name]: [message] island-chat: description: "Alternar el chat de la isla" - island-on: "&a Chat de isla activado" - island-off: "&c Chat de isla desactivado" - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat de isla activado + island-off: Chat de isla desactivado + syntax: [island] [name]: [message] spy: description: "Alternar espiar en todos los chats de la isla" - spy-on: "&a Chat espia de isla activado" - spy-off: "&c Chat espia de isla desactivado" - syntax: "&c[island-chat-spy] &4[name]&8: &f[message]" + spy-on: Chat espia de isla activado + spy-off: Chat espia de isla desactivado + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index c365970..355489e 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "activer/désactiver le chat d'équipe" - chat-on: "&a Chat d'équipe activé." - chat-off: "&a Chat d'équipe désactivé." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: "Chat d'équipe activé." + chat-off: "Chat d'équipe désactivé." + syntax: "[team] [name]: [message]" mute: description: "activer/désactiver la mise en sourdine des messages du chat d'équipe" - mute-on: "&c Chat d'équipe mis en sourdine." - mute-off: "&a Chat d'équipe sorti de sourdine." - spy: + mute-on: "Chat d'équipe mis en sourdine." + mute-off: "Chat d'équipe sorti de sourdine." + spy: description: "activer/désactiver la surveillance de tous les chats d'équipe" - spy-on: "&a Surveillance des chats d'équipe activée." - spy-off: "&c Surveillance des chats d'équipe désactivée." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: "Surveillance des chats d'équipe activée." + spy-off: "Surveillance des chats d'équipe désactivée." + syntax: "[team-chat-spy] [name]: [message]" island-chat: description: "activer/désactiver le chat d'île" - island-on: "&a Chat d'île activé." - island-off: "&c Chat d'île désactivé." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: "Chat d'île activé." + island-off: "Chat d'île désactivé." + syntax: "[island] [name]: [message]" spy: description: "activer/désactiver la surveillance de tous les chats d'île" - spy-on: "&a Surveillance des chats d'île activée." - spy-off: "&c Surveillance des chats d'île désactivée." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: "Surveillance des chats d'île activée." + spy-off: "Surveillance des chats d'île désactivée." + syntax: "[island-chat-spy] [name]: [message]" diff --git a/src/main/resources/locales/hr.yml b/src/main/resources/locales/hr.yml index 1a8726c..6b215a8 100644 --- a/src/main/resources/locales/hr.yml +++ b/src/main/resources/locales/hr.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "uključuje/isključuje timski chat" - chat-on: "&a Timski chat uključen." - chat-off: "&a Timski chat isključen." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Timski chat uključen. + chat-off: Timski chat isključen. + syntax: [team] [name]: [message] mute: description: "uključuje/isključuje nijeme poruke timskog chata" - mute-on: "&c Timski chat je utišan." - mute-off: "&a Timski chat više nije utišan." + mute-on: Timski chat je utišan. + mute-off: Timski chat više nije utišan. spy: description: "uključuje/isključuje praćenje svih timskih chatova" - spy-on: "&a Praćenje timskog chata uključeno." - spy-off: "&c Praćenje timskog chata isključeno." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Praćenje timskog chata uključeno. + spy-off: Praćenje timskog chata isključeno. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "uključuje/isključuje chat otoka" - island-on: "&a Chat otoka uključen." - island-off: "&c Chat otoka isključen." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat otoka uključen. + island-off: Chat otoka isključen. + syntax: [island] [name]: [message] spy: description: "uključuje/isključuje praćenje svih chatova otoka" - spy-on: "&a Praćenje chata otoka uključeno." - spy-off: "&c Praćenje chata otoka isključeno." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Praćenje chata otoka uključeno. + spy-off: Praćenje chata otoka isključeno. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml index e43b7eb..eaf5304 100644 --- a/src/main/resources/locales/hu.yml +++ b/src/main/resources/locales/hu.yml @@ -1,25 +1,25 @@ chat: team-chat: description: csapat chat állítása be/ki - chat-on: '&a Csapat chat be.' - chat-off: '&a Csapat chat ki.' - syntax: '&6[team] &a[name]&8: &f[message]' + chat-on: Csapat chat be. + chat-off: Csapat chat ki. + syntax: [team] [name]: [message] mute: description: csapat chat üzenetek elnémítása be/ki - mute-on: '&c Csapat chat elnémítva.' - mute-off: '&a Csapat chat elnémítás feloldva.' + mute-on: Csapat chat elnémítva. + mute-off: Csapat chat elnémítás feloldva. spy: description: kémkedés állítása az összes csapat chatben - spy-on: '&a Csapat chat kémkedés be.' - spy-off: '&c Csapat chat kémkedés ki.' - syntax: '&c[team-chat-spy] &a[name]&8: [message]' + spy-on: Csapat chat kémkedés be. + spy-off: Csapat chat kémkedés ki. + syntax: [team-chat-spy] [name]: [message] island-chat: description: sziget chat állítása be/ki - island-on: '&a Sziget chat be.' - island-off: '&c Sziget chat ki.' - syntax: '&6[island] &a[name]&8: &f[message]' + island-on: Sziget chat be. + island-off: Sziget chat ki. + syntax: [island] [name]: [message] spy: description: kémkedés állítása az összes sziget chatben - spy-on: '&a Sziget chat kémkedés be.' - spy-off: '&c Sziget chat kémkedés ki.' - syntax: '&c [island-chat-spy] &a[name]&8: &f[message]' + spy-on: Sziget chat kémkedés be. + spy-off: Sziget chat kémkedés ki. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/id.yml b/src/main/resources/locales/id.yml index 2108af3..529054d 100644 --- a/src/main/resources/locales/id.yml +++ b/src/main/resources/locales/id.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "nyala/matikan percakapan tim" - chat-on: "&a Percakapan tim dinyalakan." - chat-off: "&a Percakapan tim dimatikan." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Percakapan tim dinyalakan. + chat-off: Percakapan tim dimatikan. + syntax: [team] [name]: [message] mute: description: "nyala/matikan senyap pesan percakapan tim" - mute-on: "&c Percakapan tim dibisukan." - mute-off: "&a Percakapan tim tidak lagi dibisukan." - spy: + mute-on: Percakapan tim dibisukan. + mute-off: Percakapan tim tidak lagi dibisukan. + spy: description: "nyala/matikan pengawasan pada semua percakapan tim" - spy-on: "&a Pengawasan percakapan tim dinyalakan." - spy-off: "&c Pengawasan percakapan tim dimatikan." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Pengawasan percakapan tim dinyalakan. + spy-off: Pengawasan percakapan tim dimatikan. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "nyala/matikan percakapan pulau" - island-on: "&a Percakapan pulau dinyalakan." - island-off: "&c Percakapan pulau dimatikan." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Percakapan pulau dinyalakan. + island-off: Percakapan pulau dimatikan. + syntax: [island] [name]: [message] spy: description: "nyala/matikan pengawasan pada semua percakapan pulau" - spy-on: "&a Pengawasan percakapan pulau dinyalakan." - spy-off: "&c Pengawasan percakapan pulau dimatikan." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Pengawasan percakapan pulau dinyalakan. + spy-off: Pengawasan percakapan pulau dimatikan. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/it.yml b/src/main/resources/locales/it.yml index 3c63052..68f9fdd 100644 --- a/src/main/resources/locales/it.yml +++ b/src/main/resources/locales/it.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "attiva/disattiva la chat del team" - chat-on: "&a Chat del team attivata." - chat-off: "&a Chat del team disattivata." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Chat del team attivata. + chat-off: Chat del team disattivata. + syntax: [team] [name]: [message] mute: description: "attiva/disattiva il silenziamento dei messaggi della chat del team" - mute-on: "&c Chat del team silenziata." - mute-off: "&a Chat del team non più silenziata." + mute-on: Chat del team silenziata. + mute-off: Chat del team non più silenziata. spy: description: "attiva/disattiva la spia su tutte le chat del team" - spy-on: "&a Spia chat del team attivata." - spy-off: "&c Spia chat del team disattivata." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Spia chat del team attivata. + spy-off: Spia chat del team disattivata. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "attiva/disattiva la chat dell'isola" - island-on: "&a Chat dell'isola attivata." - island-off: "&c Chat dell'isola disattivata." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat dell'isola attivata. + island-off: Chat dell'isola disattivata. + syntax: [island] [name]: [message] spy: description: "attiva/disattiva la spia su tutte le chat dell'isola" - spy-on: "&a Spia chat dell'isola attivata." - spy-off: "&c Spia chat dell'isola disattivata." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Spia chat dell'isola attivata. + spy-off: Spia chat dell'isola disattivata. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/ja.yml b/src/main/resources/locales/ja.yml index 3058507..6b91a7c 100644 --- a/src/main/resources/locales/ja.yml +++ b/src/main/resources/locales/ja.yml @@ -2,25 +2,25 @@ chat: team-chat: description: チームチャットのオン/オフを切り替えます - chat-on: "&a チームチャット オン." - chat-off: "&a チームチャット オフ." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: チームチャット オン. + chat-off: チームチャット オフ. + syntax: [team] [name]: [message] mute: description: チームチャットメッセージのミュートを切り替えます - mute-on: "&c チームチャットをミュートしました." - mute-off: "&a チームチャットのミュートを解除しました." + mute-on: チームチャットをミュートしました. + mute-off: チームチャットのミュートを解除しました. spy: description: すべてのチームチャットのスパイを切り替えます - spy-on: "&a チームチャットスパイ オン." - spy-off: "&c チームチャットスパイ オフ." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: チームチャットスパイ オン. + spy-off: チームチャットスパイ オフ. + syntax: [team-chat-spy] [name]: [message] island-chat: description: アイランドチャットのオン/オフを切り替えます - island-on: "&a アイランドチャット オン." - island-off: "&c アイランドチャット オフ." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: アイランドチャット オン. + island-off: アイランドチャット オフ. + syntax: [island] [name]: [message] spy: description: すべての島のチャットのスパイを切り替えます - spy-on: "&a アイランドチャットスパイ オン." - spy-off: "&c アイランドチャットスパイ オフ." - syntax: "&c[island-chat-spy] &a[name]&8: &f[message]" + spy-on: アイランドチャットスパイ オン. + spy-off: アイランドチャットスパイ オフ. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml index 97d3e2e..0c5dbd7 100644 --- a/src/main/resources/locales/ko.yml +++ b/src/main/resources/locales/ko.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "팀 채팅 켜기/끄기 전환" - chat-on: "&a 팀 채팅이 켜졌습니다." - chat-off: "&a 팀 채팅이 꺼졌습니다." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: 팀 채팅이 켜졌습니다. + chat-off: 팀 채팅이 꺼졌습니다. + syntax: [team] [name]: [message] mute: description: "팀 채팅 메시지 음소거 전환" - mute-on: "&c 팀 채팅이 음소거되었습니다." - mute-off: "&a 팀 채팅 음소거가 해제되었습니다." + mute-on: 팀 채팅이 음소거되었습니다. + mute-off: 팀 채팅 음소거가 해제되었습니다. spy: description: "모든 팀 채팅 감시 켜기/끄기 전환" - spy-on: "&a 팀 채팅 감시가 켜졌습니다." - spy-off: "&c 팀 채팅 감시가 꺼졌습니다." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: 팀 채팅 감시가 켜졌습니다. + spy-off: 팀 채팅 감시가 꺼졌습니다. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "섬 채팅 켜기/끄기 전환" - island-on: "&a 섬 채팅이 켜졌습니다." - island-off: "&c 섬 채팅이 꺼졌습니다." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: 섬 채팅이 켜졌습니다. + island-off: 섬 채팅이 꺼졌습니다. + syntax: [island] [name]: [message] spy: description: "모든 섬 채팅 감시 켜기/끄기 전환" - spy-on: "&a 섬 채팅 감시가 켜졌습니다." - spy-off: "&c 섬 채팅 감시가 꺼졌습니다." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: 섬 채팅 감시가 켜졌습니다. + spy-off: 섬 채팅 감시가 꺼졌습니다. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index d5d1b3b..575393c 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "ieslēdz/izslēdz komandas tērzēšanu" - chat-on: "&a Komandas tērzēšana ieslēgta." - chat-off: "&a Komandas tērzēšana izslēgta." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Komandas tērzēšana ieslēgta. + chat-off: Komandas tērzēšana izslēgta. + syntax: [team] [name]: [message] mute: description: "ieslēdz/izslēdz komandas tērzēšanas ziņojumu apklusināšanu" - mute-on: "&c Komandas tērzēšana apklusināta." - mute-off: "&a Komandas tērzēšanas apklusināšana atcelta." + mute-on: Komandas tērzēšana apklusināta. + mute-off: Komandas tērzēšanas apklusināšana atcelta. spy: description: "ieslēdz/izslēdz visas komandas tērzēšanas uzraudzību" - spy-on: "&a Komandas tērzēšanas uzraudzība ieslēgta." - spy-off: "&c Komandas tērzēšanas uzraudzība izslēgta." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Komandas tērzēšanas uzraudzība ieslēgta. + spy-off: Komandas tērzēšanas uzraudzība izslēgta. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "ieslēdz/izslēdz salas tērzēšanu" - island-on: "&a Salas tērzēšana ieslēgta." - island-off: "&c Salas tērzēšana izslēgta." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Salas tērzēšana ieslēgta. + island-off: Salas tērzēšana izslēgta. + syntax: [island] [name]: [message] spy: description: "ieslēdz/izslēdz visu salu tērzēšanas uzraudzību" - spy-on: "&a Salas tērzēšanas uzraudzība ieslēgta." - spy-off: "&c Salas tērzēšanas uzraudzība izslēgta." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Salas tērzēšanas uzraudzība ieslēgta. + spy-off: Salas tērzēšanas uzraudzība izslēgta. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/nl.yml b/src/main/resources/locales/nl.yml index 57c9809..42fcf51 100644 --- a/src/main/resources/locales/nl.yml +++ b/src/main/resources/locales/nl.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "zet teamchat aan/uit" - chat-on: "&a Teamchat aan." - chat-off: "&a Teamchat uit." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Teamchat aan. + chat-off: Teamchat uit. + syntax: [team] [name]: [message] mute: description: "zet het dempen van teamchat berichten aan/uit" - mute-on: "&c Teamchat gedempt." - mute-off: "&a Teamchat niet meer gedempt." + mute-on: Teamchat gedempt. + mute-off: Teamchat niet meer gedempt. spy: description: "zet het bespioneren van alle teamchats aan/uit" - spy-on: "&a Teamchat spion aan." - spy-off: "&c Teamchat spion uit." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Teamchat spion aan. + spy-off: Teamchat spion uit. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "zet eilandchat aan/uit" - island-on: "&a Eilandchat aan." - island-off: "&c Eilandchat uit." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Eilandchat aan. + island-off: Eilandchat uit. + syntax: [island] [name]: [message] spy: description: "zet het bespioneren van alle eilandchats aan/uit" - spy-on: "&a Eilandchat spion aan." - spy-off: "&c Eilandchat spion uit." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Eilandchat spion aan. + spy-off: Eilandchat spion uit. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml index b6ab094..038ea4a 100644 --- a/src/main/resources/locales/pl.yml +++ b/src/main/resources/locales/pl.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "włącza/wyłącza czat drużynowy" - chat-on: "&a Czat drużynowy włączony." - chat-off: "&a Czat drużynowy wyłączony." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Czat drużynowy włączony. + chat-off: Czat drużynowy wyłączony. + syntax: [team] [name]: [message] mute: description: "włącza/wyłącza wyciszanie wiadomości czatu drużynowego" - mute-on: "&c Czat drużynowy wyciszony." - mute-off: "&a Czat drużynowy odciszony." + mute-on: Czat drużynowy wyciszony. + mute-off: Czat drużynowy odciszony. spy: description: "włącza/wyłącza szpiegowanie wszystkich czatów drużynowych" - spy-on: "&a Szpiegowanie czatu drużynowego włączone." - spy-off: "&c Szpiegowanie czatu drużynowego wyłączone." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Szpiegowanie czatu drużynowego włączone. + spy-off: Szpiegowanie czatu drużynowego wyłączone. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "włącza/wyłącza czat wyspy" - island-on: "&a Czat wyspy włączony." - island-off: "&c Czat wyspy wyłączony." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Czat wyspy włączony. + island-off: Czat wyspy wyłączony. + syntax: [island] [name]: [message] spy: description: "włącza/wyłącza szpiegowanie wszystkich czatów wyspy" - spy-on: "&a Szpiegowanie czatu wyspy włączone." - spy-off: "&c Szpiegowanie czatu wyspy wyłączone." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Szpiegowanie czatu wyspy włączone. + spy-off: Szpiegowanie czatu wyspy wyłączone. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/pt-BR.yml b/src/main/resources/locales/pt-BR.yml index cc6add7..b7ad454 100644 --- a/src/main/resources/locales/pt-BR.yml +++ b/src/main/resources/locales/pt-BR.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "ativa/desativa o chat da equipe" - chat-on: "&a Chat da equipe ativado." - chat-off: "&a Chat da equipe desativado." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Chat da equipe ativado. + chat-off: Chat da equipe desativado. + syntax: [team] [name]: [message] mute: description: "ativa/desativa o silenciamento de mensagens do chat da equipe" - mute-on: "&c Chat da equipe silenciado." - mute-off: "&a Chat da equipe não está mais silenciado." + mute-on: Chat da equipe silenciado. + mute-off: Chat da equipe não está mais silenciado. spy: description: "ativa/desativa a espionagem de todos os chats da equipe" - spy-on: "&a Espião de chat da equipe ativado." - spy-off: "&c Espião de chat da equipe desativado." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Espião de chat da equipe ativado. + spy-off: Espião de chat da equipe desativado. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "ativa/desativa o chat da ilha" - island-on: "&a Chat da ilha ativado." - island-off: "&c Chat da ilha desativado." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat da ilha ativado. + island-off: Chat da ilha desativado. + syntax: [island] [name]: [message] spy: description: "ativa/desativa a espionagem de todos os chats da ilha" - spy-on: "&a Espião de chat da ilha ativado." - spy-off: "&c Espião de chat da ilha desativado." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Espião de chat da ilha ativado. + spy-off: Espião de chat da ilha desativado. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/pt.yml b/src/main/resources/locales/pt.yml index 12d8d94..6d94470 100644 --- a/src/main/resources/locales/pt.yml +++ b/src/main/resources/locales/pt.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "ativa/desativa o chat da equipa" - chat-on: "&a Chat da equipa ativado." - chat-off: "&a Chat da equipa desativado." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Chat da equipa ativado. + chat-off: Chat da equipa desativado. + syntax: [team] [name]: [message] mute: description: "ativa/desativa o silenciamento de mensagens do chat da equipa" - mute-on: "&c Chat da equipa silenciado." - mute-off: "&a Chat da equipa não está mais silenciado." + mute-on: Chat da equipa silenciado. + mute-off: Chat da equipa não está mais silenciado. spy: description: "ativa/desativa a espionagem de todos os chats da equipa" - spy-on: "&a Espião de chat da equipa ativado." - spy-off: "&c Espião de chat da equipa desativado." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Espião de chat da equipa ativado. + spy-off: Espião de chat da equipa desativado. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "ativa/desativa o chat da ilha" - island-on: "&a Chat da ilha ativado." - island-off: "&c Chat da ilha desativado." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat da ilha ativado. + island-off: Chat da ilha desativado. + syntax: [island] [name]: [message] spy: description: "ativa/desativa a espionagem de todos os chats da ilha" - spy-on: "&a Espião de chat da ilha ativado." - spy-off: "&c Espião de chat da ilha desativado." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Espião de chat da ilha ativado. + spy-off: Espião de chat da ilha desativado. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml index 26b1434..a369060 100644 --- a/src/main/resources/locales/ro.yml +++ b/src/main/resources/locales/ro.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "activează/dezactivează chat-ul de echipă" - chat-on: "&a Chat de echipă activat." - chat-off: "&a Chat de echipă dezactivat." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Chat de echipă activat. + chat-off: Chat de echipă dezactivat. + syntax: [team] [name]: [message] mute: description: "activează/dezactivează dezactivarea sunetului chat-ului de echipă" - mute-on: "&c Chat de echipă dezactivat sonor." - mute-off: "&a Chat de echipă reactivat sonor." + mute-on: Chat de echipă dezactivat sonor. + mute-off: Chat de echipă reactivat sonor. spy: description: "activează/dezactivează spionarea tuturor chat-urilor de echipă" - spy-on: "&a Spion chat de echipă activat." - spy-off: "&c Spion chat de echipă dezactivat." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Spion chat de echipă activat. + spy-off: Spion chat de echipă dezactivat. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "activează/dezactivează chat-ul de insulă" - island-on: "&a Chat de insulă activat." - island-off: "&c Chat de insulă dezactivat." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Chat de insulă activat. + island-off: Chat de insulă dezactivat. + syntax: [island] [name]: [message] spy: description: "activează/dezactivează spionarea tuturor chat-urilor de insulă" - spy-on: "&a Spion chat de insulă activat." - spy-off: "&c Spion chat de insulă dezactivat." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Spion chat de insulă activat. + spy-off: Spion chat de insulă dezactivat. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml index 65802fd..7a1ab9f 100644 --- a/src/main/resources/locales/ru.yml +++ b/src/main/resources/locales/ru.yml @@ -1,25 +1,30 @@ +# ######################################################################################## # +# Это YML файл. Будьте осторожны при редактировании. Проверяйте свои правки # +# в YAML валидаторе, например, на http://yaml-online-parser.appspot.com # +# ######################################################################################## # + chat: team-chat: - description: "включает/выключает командный чат" - chat-on: "&a Командный чат включён." - chat-off: "&a Командный чат выключен." - syntax: "&6[team] &a[name]&8: &f[message]" + description: включает/отключает командный чат + chat-on: Командный чат включен. + chat-off: Командный чат отключен. + syntax: [team] [name]: [message] mute: - description: "включает/выключает отключение звука командного чата" - mute-on: "&c Командный чат заглушен." - mute-off: "&a Командный чат разглушен." + description: включает/отключает отключение звука командного чата + mute-on: Командный чат заглушен. + mute-off: Командный чат разглушен. spy: - description: "включает/выключает слежку за всеми командными чатами" - spy-on: "&a Слежка за командным чатом включена." - spy-off: "&c Слежка за командным чатом выключена." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + description: переключает шпионаж на все командные чаты + spy-on: Слежка за командным чатам включена. + spy-off: Слежка за командным чатам отключена. + syntax: [team-chat-spy] [name]: [message] island-chat: - description: "включает/выключает чат острова" - island-on: "&a Чат острова включён." - island-off: "&c Чат острова выключен." - syntax: "&6[island] &a[name]&8: &f[message]" + description: включает/отключает островной чат + island-on: Островной чат включен. + island-off: Островной чат отключен. + syntax: [island] [name]: [message] spy: - description: "включает/выключает слежку за всеми чатами острова" - spy-on: "&a Слежка за чатом острова включена." - spy-off: "&c Слежка за чатом острова выключена." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + description: переключает шпионаж на все островные чаты + spy-on: Слежка за островным чатом включена. + spy-off: Слежка за островным чатом отключена. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/tr.yml b/src/main/resources/locales/tr.yml index 22a35bd..1b6ca03 100644 --- a/src/main/resources/locales/tr.yml +++ b/src/main/resources/locales/tr.yml @@ -5,25 +5,25 @@ meta: chat: team-chat: description: "ada takım sohbetini açar/kapatır" - chat-on: "&aTakım sohbeti aktifleştirildi." - chat-off: "&cTakım sohbeti kapatıldı." - syntax: "&6[team] &a[name] &8: &f[message]" + chat-on: Takım sohbeti aktifleştirildi. + chat-off: Takım sohbeti kapatıldı. + syntax: [team] [name] : [message] mute: description: "takım sohbeti mesajlarını susturmayı açar/kapatır" - mute-on: "&cTakım sohbeti susturuldu." - mute-off: "&aTakım sohbeti susturma kaldırıldı." + mute-on: Takım sohbeti susturuldu. + mute-off: Takım sohbeti susturma kaldırıldı. spy: description: "takım sohbetlerini görmeni sağlar" - spy-on: "&aTakım sohbetlerini artık görebiliyorsun." - spy-off: "&cTakım sohbetlerini artık göremiyorsun." - syntax: "&c[team-chat-spy] &a[name]&8: &f[message]" + spy-on: Takım sohbetlerini artık görebiliyorsun. + spy-off: Takım sohbetlerini artık göremiyorsun. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "ada sohbetini açar/kapatır" - island-on: "&aAda sohbeti aktifleştirildi." - island-off: "&cAda sohbeti kapatıldı." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Ada sohbeti aktifleştirildi. + island-off: Ada sohbeti kapatıldı. + syntax: [island] [name]: [message] spy: description: "ada sohbetlerini görmeni sağlar" - spy-on: "&aAda sohbetlerini artık görebiliyorsun." - spy-off: "&cAda sohbetlerini artık göremiyorsun." - syntax: "&c[island-chat-spy] &a[name]&8: &f[message]" + spy-on: Ada sohbetlerini artık görebiliyorsun. + spy-off: Ada sohbetlerini artık göremiyorsun. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/uk.yml b/src/main/resources/locales/uk.yml index b307cd3..e3072ad 100644 --- a/src/main/resources/locales/uk.yml +++ b/src/main/resources/locales/uk.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "вмикає/вимикає командний чат" - chat-on: "&a Командний чат увімкнено." - chat-off: "&a Командний чат вимкнено." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: Командний чат увімкнено. + chat-off: Командний чат вимкнено. + syntax: [team] [name]: [message] mute: description: "вмикає/вимикає ігнорування повідомлень командного чату" - mute-on: "&c Командний чат заглушено." - mute-off: "&a Командний чат розглушено." + mute-on: Командний чат заглушено. + mute-off: Командний чат розглушено. spy: description: "вмикає/вимикає стеження за всіма командними чатами" - spy-on: "&a Стеження за командним чатом увімкнено." - spy-off: "&c Стеження за командним чатом вимкнено." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: Стеження за командним чатом увімкнено. + spy-off: Стеження за командним чатом вимкнено. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "вмикає/вимикає чат острова" - island-on: "&a Чат острова увімкнено." - island-off: "&c Чат острова вимкнено." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: Чат острова увімкнено. + island-off: Чат острова вимкнено. + syntax: [island] [name]: [message] spy: description: "вмикає/вимикає стеження за всіма чатами острова" - spy-on: "&a Стеження за чатом острова увімкнено." - spy-off: "&c Стеження за чатом острова вимкнено." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: Стеження за чатом острова увімкнено. + spy-off: Стеження за чатом острова вимкнено. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/vi.yml b/src/main/resources/locales/vi.yml index 2d6595c..6bcbf78 100644 --- a/src/main/resources/locales/vi.yml +++ b/src/main/resources/locales/vi.yml @@ -1,25 +1,25 @@ chat: team-chat: description: bật/tắt chat đội - chat-on: '&aĐã bật chat đội.' - chat-off: '&aĐã tắt chat đội.' - syntax: '&6[team] &a[name]&8: &f[message]' + chat-on: Đã bật chat đội. + chat-off: Đã tắt chat đội. + syntax: [team] [name]: [message] mute: description: bật/tắt tắt tiếng tin nhắn chat đội - mute-on: '&cĐã tắt tiếng chat đội.' - mute-off: '&aĐã bỏ tắt tiếng chat đội.' + mute-on: Đã tắt tiếng chat đội. + mute-off: Đã bỏ tắt tiếng chat đội. spy: description: bật/tắt giám sát chat đội - spy-on: '&aĐã bật giám sát chat đội.' - spy-off: '&cĐã tắt giám sát chat đội.' - syntax: '&c[team-chat-spy] &a[name]&8: [message]' + spy-on: Đã bật giám sát chat đội. + spy-off: Đã tắt giám sát chat đội. + syntax: [team-chat-spy] [name]: [message] island-chat: description: bật/tắt chat đảo - island-on: '&aĐã bật chat đảo.' - island-off: '&cĐã tắt chat đảo.' - syntax: '&6[island] &a[name]&8: &f[message]' + island-on: Đã bật chat đảo. + island-off: Đã tắt chat đảo. + syntax: [island] [name]: [message] spy: description: bật/tắt giám sát chat đảo - spy-on: '&aĐã bật giám sát chat đảo.' - spy-off: '&cĐã tắt giám sát chat đảo.' - syntax: '&c [island-chat-spy] &a[name]&8: &f[message]' + spy-on: Đã bật giám sát chat đảo. + spy-off: Đã tắt giám sát chat đảo. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index effed47..8178307 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "切换团队聊天开闭" - chat-on: "&a 团队聊天已开启." - chat-off: "&a 团队聊天已关闭." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: 团队聊天已开启. + chat-off: 团队聊天已关闭. + syntax: [team] [name]: [message] mute: description: "切换静音团队聊天消息" - mute-on: "&c 团队聊天已静音." - mute-off: "&a 团队聊天已取消静音." - spy: + mute-on: 团队聊天已静音. + mute-off: 团队聊天已取消静音. + spy: description: "切换偷听所有团队聊天" - spy-on: "&a 团队聊天偷听已开启." - spy-off: "&c 团队聊天偷听已关闭." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: 团队聊天偷听已开启. + spy-off: 团队聊天偷听已关闭. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "切换岛屿聊天开闭" - island-on: "&a 岛屿聊天已开启." - island-off: "&c 岛屿聊天已关闭." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: 岛屿聊天已开启. + island-off: 岛屿聊天已关闭. + syntax: [island] [name]: [message] spy: description: "切换偷听所有岛屿聊天" - spy-on: "&a 岛屿聊天偷听已开启." - spy-off: "&c 岛屿聊天偷听已关闭." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: 岛屿聊天偷听已开启. + spy-off: 岛屿聊天偷听已关闭. + syntax: [island-chat-spy] [name]: [message] diff --git a/src/main/resources/locales/zh-HK.yml b/src/main/resources/locales/zh-HK.yml index 31b30e0..bce420e 100644 --- a/src/main/resources/locales/zh-HK.yml +++ b/src/main/resources/locales/zh-HK.yml @@ -1,25 +1,25 @@ chat: team-chat: description: "切換團隊聊天開關" - chat-on: "&a 團隊聊天已開啟." - chat-off: "&a 團隊聊天已關閉." - syntax: "&6[team] &a[name]&8: &f[message]" + chat-on: 團隊聊天已開啟. + chat-off: 團隊聊天已關閉. + syntax: [team] [name]: [message] mute: description: "切換靜音團隊聊天訊息" - mute-on: "&c 團隊聊天已靜音." - mute-off: "&a 團隊聊天已取消靜音." + mute-on: 團隊聊天已靜音. + mute-off: 團隊聊天已取消靜音. spy: description: "切換監視所有團隊聊天" - spy-on: "&a 團隊聊天監視已開啟." - spy-off: "&c 團隊聊天監視已關閉." - syntax: "&c[team-chat-spy] &a[name]&8: [message]" + spy-on: 團隊聊天監視已開啟. + spy-off: 團隊聊天監視已關閉. + syntax: [team-chat-spy] [name]: [message] island-chat: description: "切換島嶼聊天開關" - island-on: "&a 島嶼聊天已開啟." - island-off: "&c 島嶼聊天已關閉." - syntax: "&6[island] &a[name]&8: &f[message]" + island-on: 島嶼聊天已開啟. + island-off: 島嶼聊天已關閉. + syntax: [island] [name]: [message] spy: description: "切換監視所有島嶼聊天" - spy-on: "&a 島嶼聊天監視已開啟." - spy-off: "&c 島嶼聊天監視已關閉." - syntax: "&c [island-chat-spy] &a[name]&8: &f[message]" + spy-on: 島嶼聊天監視已開啟. + spy-off: 島嶼聊天監視已關閉. + syntax: [island-chat-spy] [name]: [message] From 23ba34468411a5a62c048c62a9803f9e51976489 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:36:02 -0700 Subject: [PATCH 25/36] Bump version to 1.4.0 Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0aba550..a42d049 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ -LOCAL - 1.3.1 + 1.4.0 From bdb7c7f7f685af37626e8225141ce4ffdb68d855 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:40:34 -0700 Subject: [PATCH 26/36] Enable snapshots on bentoboxworld repo to resolve BentoBox 3.x SNAPSHOTs The bentoboxworld repository on CodeMC hosts BentoBox 3.14.0-SNAPSHOT but snapshots were disabled, causing CI dependency resolution to fail. Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a42d049..522e7b7 100644 --- a/pom.xml +++ b/pom.xml @@ -124,12 +124,12 @@ true true - + bentoboxworld https://repo.codemc.org/repository/bentoboxworld/ true - false + true From 1e478fbda019eb00e862b56e9874afc0b286b58a Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:45:35 -0700 Subject: [PATCH 27/36] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 522e7b7..3833eed 100644 --- a/pom.xml +++ b/pom.xml @@ -188,6 +188,12 @@ ${mockito.version} test + + org.mockito + mockito-inline + ${mockito.version} + test + org.mockito mockito-junit-jupiter From 5017c71d44610b4736e5781a7f1f4753be5cc9c2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:45:48 -0700 Subject: [PATCH 28/36] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../bentobox/chat/listeners/ChatListener.java | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index fbc02f4..7a2300e 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -65,10 +65,9 @@ public void execute(Listener listener, Event e) { onChat((AsyncPlayerChatEvent) e); } - public void onChat(final AsyncPlayerChatEvent e) { - - Player p = e.getPlayer(); - World playerWorld = e.getPlayer().getWorld(); + private boolean handleChatSync(Player p, String message) { + boolean handled = false; + World playerWorld = p.getWorld(); // Determine the worlds to use for team chat List teamChatWorlds = new ArrayList<>(); @@ -81,41 +80,52 @@ public void onChat(final AsyncPlayerChatEvent e) { if (teamChatWorlds.isEmpty()) { addon.getChatWorld().ifPresent(teamChatWorlds::add); } - // If still empty, nothing to do - if (teamChatWorlds.isEmpty()) { - return; - } } // Process team chat for all matching worlds. // If multiple game modes cover the same extra world, chat goes to all matching teams. - if (teamChatUsers.contains(p.getUniqueId())) { + if (!teamChatWorlds.isEmpty() && teamChatUsers.contains(p.getUniqueId())) { for (World w : teamChatWorlds) { if (addon.getIslands().inTeam(w, p.getUniqueId())) { - // Cancel the event - e.setCancelled(true); - if (e.isAsynchronous()) { - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> teamChat(w, p, e.getMessage())); - } else { - teamChat(w, p, e.getMessage()); - } + handled = true; + teamChat(w, p, message); } } } // Island chat - uses physical location, only meaningful if player is on an island - addon.getIslands().getIslandAt(p.getLocation()) - .filter(islandChatters.keySet()::contains) - .filter(i -> islandChatters.get(i).contains(p)) - .ifPresent(i -> { - // Cancel the event - e.setCancelled(true); - if (e.isAsynchronous()) { - Bukkit.getScheduler().runTask(addon.getPlugin(), () -> islandChat(i, p, e.getMessage())); - } else { - islandChat(i, p, e.getMessage()); + Island island = addon.getIslands().getIslandAt(p.getLocation()) + .filter(islandChatters.keySet()::contains) + .filter(i -> islandChatters.get(i).contains(p)) + .orElse(null); + if (island != null) { + handled = true; + islandChat(island, p, message); + } + + return handled; + } + + public void onChat(final AsyncPlayerChatEvent e) { + + Player p = e.getPlayer(); + String message = e.getMessage(); + + if (e.isAsynchronous()) { + try { + Boolean handled = Bukkit.getScheduler().callSyncMethod(addon.getPlugin(), + () -> handleChatSync(p, message)).get(); + if (Boolean.TRUE.equals(handled)) { + e.setCancelled(true); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } catch (java.util.concurrent.ExecutionException ex) { + throw new RuntimeException("Failed to process async chat on the main thread", ex); } - }); + } else if (handleChatSync(p, message)) { + e.setCancelled(true); + } } // Removes player from TeamChat set if he left the island From 0a65e79269eae54f28784c3bc7a54a2fd1382c7c Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Apr 2026 23:46:18 -0700 Subject: [PATCH 29/36] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 3833eed..1ec4b12 100644 --- a/pom.xml +++ b/pom.xml @@ -281,6 +281,7 @@ --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.logging/java.util.logging=ALL-UNNAMED + --add-opens jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.base/java.lang.ref=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.zip=ALL-UNNAMED From cf8d83afd5860994cbec9dee5abeb56d56815806 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 06:47:22 +0000 Subject: [PATCH 30/36] Initial plan From 45b9d4eb0f73a809dca82e40e4628dc54a8cd9a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 06:51:04 +0000 Subject: [PATCH 31/36] Fix island chat to use isLogIslandChats() and [Island Chat Log] prefix Agent-Logs-Url: https://github.com/BentoBoxWorld/Chat/sessions/12e3f697-a2ad-4956-a392-972c6517d421 Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com> --- .../java/world/bentobox/chat/listeners/ChatListener.java | 4 ++-- .../world/bentobox/chat/listeners/ChatListenerTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 7a2300e..2657963 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -148,8 +148,8 @@ public void islandChat(Island i, Player player, String message) { // Send message to island .forEach(u -> u.sendMessage("chat.island-chat.syntax", TextVariables.NAME, player.getName(), MESSAGE, message)); // Log if required - if (addon.getSettings().isLogTeamChats()) { - addon.log("[Team Chat Log] " + player.getName() + ": " + message); + if (addon.getSettings().isLogIslandChats()) { + addon.log("[Island Chat Log] " + player.getName() + ": " + message); } // Spy if required Bukkit.getOnlinePlayers().stream() diff --git a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java index e33531d..0501a88 100644 --- a/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java +++ b/src/test/java/world/bentobox/chat/listeners/ChatListenerTest.java @@ -321,16 +321,16 @@ public void testTeamChatDoesNotLogWhenDisabled() { @Test public void testIslandChatLogsWhenEnabled() { - when(settings.isLogTeamChats()).thenReturn(true); + when(settings.isLogIslandChats()).thenReturn(true); mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); listener.islandChat(island, player, "island log message"); - verify(addon).log("[Team Chat Log] tastybento: island log message"); + verify(addon).log("[Island Chat Log] tastybento: island log message"); } @Test public void testIslandChatDoesNotLogWhenDisabled() { - when(settings.isLogTeamChats()).thenReturn(false); + when(settings.isLogIslandChats()).thenReturn(false); mockedBukkit.when(org.bukkit.Bukkit::getOnlinePlayers).thenReturn(Collections.emptyList()); listener.islandChat(island, player, "island silent message"); From fd15f97f202b853a2a5bf1ff17fcde9ffbd01cfd Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 13 Apr 2026 00:08:54 -0700 Subject: [PATCH 32/36] Fix syntax formatting for team chat spy in cs.yml --- src/main/resources/locales/cs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml index 4c79c28..efe6a25 100644 --- a/src/main/resources/locales/cs.yml +++ b/src/main/resources/locales/cs.yml @@ -14,7 +14,7 @@ chat: description: "Přepnout šmírování všech týmových chatů" spy-on: Šmírování týmových chatů zapnuto spy-off: Šmírování týmových chatů vypnuto - syntax: [team-chat-spy] [name]: [message] + syntax: "[team-chat-spy] [name]: [message]" island-chat: description: "Přepnout chat na ostrově zapnuto/vypnuto" island-on: Chat na ostrově zapnut From c13a21e3ebdbb2a99be2f7b302ab6155a18a2097 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 13 Apr 2026 00:13:11 -0700 Subject: [PATCH 33/36] =?UTF-8?q?Remove=20mockito-inline=20dependency=20?= =?UTF-8?q?=E2=80=94=20merged=20into=20mockito-core=20in=20Mockito=205.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1ec4b12..cd69f81 100644 --- a/pom.xml +++ b/pom.xml @@ -188,12 +188,6 @@ ${mockito.version} test - - org.mockito - mockito-inline - ${mockito.version} - test - org.mockito mockito-junit-jupiter From d43ec290762ae42482c2fc2284bb87f8100fb461 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 13 Apr 2026 05:28:06 -0700 Subject: [PATCH 34/36] Improve mute UX: sender echo, mute reminder, and clear mute on chat toggle-off - Muted senders now always see their own team chat message plus a reminder that team chat is muted (chat.team-chat.mute.reminder locale key) - Toggling team chat off now also clears the player's mute state, so re-enabling chat starts fresh without a silent mute persisting - Added translated reminder string to all 23 locale files Co-Authored-By: Claude Sonnet 4.6 --- .../java/world/bentobox/chat/listeners/ChatListener.java | 7 +++++++ src/main/resources/locales/cs.yml | 1 + src/main/resources/locales/de.yml | 1 + src/main/resources/locales/en-US.yml | 1 + src/main/resources/locales/es.yml | 1 + src/main/resources/locales/fr.yml | 1 + src/main/resources/locales/hr.yml | 1 + src/main/resources/locales/hu.yml | 1 + src/main/resources/locales/id.yml | 1 + src/main/resources/locales/it.yml | 1 + src/main/resources/locales/ja.yml | 1 + src/main/resources/locales/ko.yml | 1 + src/main/resources/locales/lv.yml | 1 + src/main/resources/locales/nl.yml | 1 + src/main/resources/locales/pl.yml | 1 + src/main/resources/locales/pt-BR.yml | 1 + src/main/resources/locales/pt.yml | 1 + src/main/resources/locales/ro.yml | 1 + src/main/resources/locales/ru.yml | 1 + src/main/resources/locales/tr.yml | 1 + src/main/resources/locales/uk.yml | 1 + src/main/resources/locales/vi.yml | 1 + src/main/resources/locales/zh-CN.yml | 1 + src/main/resources/locales/zh-HK.yml | 1 + 24 files changed, 30 insertions(+) diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 2657963..6da5457 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -173,6 +173,12 @@ public void teamChat(World w, final Player player, String message) { .filter(target -> !teamChatMuted.contains(target.getUniqueId())) // Send the message to them .forEach(target -> target.sendMessage("chat.team-chat.syntax", TextVariables.NAME, player.getName(), MESSAGE, message)); + // Always show the sender their own message if they have muted team chat, plus a reminder + if (teamChatMuted.contains(player.getUniqueId())) { + User sender = User.getInstance(player); + sender.sendMessage("chat.team-chat.syntax", TextVariables.NAME, player.getName(), MESSAGE, message); + sender.sendMessage("chat.team-chat.mute.reminder"); + } // Log if required if (addon.getSettings().isLogTeamChats()) { addon.log("[Team Chat Log] " + player.getName() + ": " + message); @@ -245,6 +251,7 @@ public boolean toggleIslandSpy(UUID playerUUID) { public boolean togglePlayerTeamChat(UUID playerUUID) { if (teamChatUsers.contains(playerUUID)) { teamChatUsers.remove(playerUUID); + teamChatMuted.remove(playerUUID); // clear mute when team chat is toggled off return false; } else { teamChatUsers.add(playerUUID); diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml index efe6a25..2ba31e5 100644 --- a/src/main/resources/locales/cs.yml +++ b/src/main/resources/locales/cs.yml @@ -10,6 +10,7 @@ chat: description: "Přepnout ztlumení zpráv týmového chatu" mute-on: Týmový chat ztlumen. mute-off: Týmový chat odztlumen. + reminder: (Týmový chat je ztlumen - použij /teamchat mute k odtlumení) spy: description: "Přepnout šmírování všech týmových chatů" spy-on: Šmírování týmových chatů zapnuto diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index 47555a8..ddc86b1 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -9,6 +9,7 @@ chat: description: "Team Chat Stummschalten ein- oder ausschalten" mute-on: Team Chat stummgeschaltet. mute-off: Team Chat Stummschaltung aufgehoben. + reminder: (Team Chat ist stummgeschaltet - verwende /teamchat mute zum Aufheben der Stummschaltung) spy: description: "Team Chat Socialspy ein- oder ausschalten" spy-on: Team Chat Socialspy an. diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index a17e0f4..9aae296 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -8,6 +8,7 @@ chat: description: "toggles muting team chat messages" mute-on: Team chat muted. mute-off: Team chat unmuted. + reminder: (Team chat is muted - use /teamchat mute to unmute) spy: description: "toggles spying on all team chats" spy-on: Team chat spy on. diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml index 7c7ee86..d0a9222 100644 --- a/src/main/resources/locales/es.yml +++ b/src/main/resources/locales/es.yml @@ -8,6 +8,7 @@ chat: description: "Alternar silenciar mensajes del chat de equipo" mute-on: Chat de equipo silenciado. mute-off: Chat de equipo sin silencio. + reminder: (El chat de equipo está silenciado - usa /teamchat mute para desmutear) spy: description: "Alternar espiar en todos los chats de equipo" spy-on: Chat de equipo espía activado diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index 355489e..8091de4 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -8,6 +8,7 @@ chat: description: "activer/désactiver la mise en sourdine des messages du chat d'équipe" mute-on: "Chat d'équipe mis en sourdine." mute-off: "Chat d'équipe sorti de sourdine." + reminder: "(Chat d'équipe mis en sourdine - utilise /teamchat mute pour sortir de la sourdine)" spy: description: "activer/désactiver la surveillance de tous les chats d'équipe" spy-on: "Surveillance des chats d'équipe activée." diff --git a/src/main/resources/locales/hr.yml b/src/main/resources/locales/hr.yml index 6b215a8..fd09c15 100644 --- a/src/main/resources/locales/hr.yml +++ b/src/main/resources/locales/hr.yml @@ -8,6 +8,7 @@ chat: description: "uključuje/isključuje nijeme poruke timskog chata" mute-on: Timski chat je utišan. mute-off: Timski chat više nije utišan. + reminder: (Timski chat je utišan - koristi /teamchat mute za aktiviranje) spy: description: "uključuje/isključuje praćenje svih timskih chatova" spy-on: Praćenje timskog chata uključeno. diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml index eaf5304..aa88e83 100644 --- a/src/main/resources/locales/hu.yml +++ b/src/main/resources/locales/hu.yml @@ -8,6 +8,7 @@ chat: description: csapat chat üzenetek elnémítása be/ki mute-on: Csapat chat elnémítva. mute-off: Csapat chat elnémítás feloldva. + reminder: (Csapat chat elnémítva - használd a /teamchat mute parancsot az elnémítás feloldásához) spy: description: kémkedés állítása az összes csapat chatben spy-on: Csapat chat kémkedés be. diff --git a/src/main/resources/locales/id.yml b/src/main/resources/locales/id.yml index 529054d..7c9544b 100644 --- a/src/main/resources/locales/id.yml +++ b/src/main/resources/locales/id.yml @@ -8,6 +8,7 @@ chat: description: "nyala/matikan senyap pesan percakapan tim" mute-on: Percakapan tim dibisukan. mute-off: Percakapan tim tidak lagi dibisukan. + reminder: (Percakapan tim dibisukan - gunakan /teamchat mute untuk membatalkan bisuan) spy: description: "nyala/matikan pengawasan pada semua percakapan tim" spy-on: Pengawasan percakapan tim dinyalakan. diff --git a/src/main/resources/locales/it.yml b/src/main/resources/locales/it.yml index 68f9fdd..b007b18 100644 --- a/src/main/resources/locales/it.yml +++ b/src/main/resources/locales/it.yml @@ -8,6 +8,7 @@ chat: description: "attiva/disattiva il silenziamento dei messaggi della chat del team" mute-on: Chat del team silenziata. mute-off: Chat del team non più silenziata. + reminder: (Chat del team silenziata - usa /teamchat mute per togliere il silenziamento) spy: description: "attiva/disattiva la spia su tutte le chat del team" spy-on: Spia chat del team attivata. diff --git a/src/main/resources/locales/ja.yml b/src/main/resources/locales/ja.yml index 6b91a7c..cb1434f 100644 --- a/src/main/resources/locales/ja.yml +++ b/src/main/resources/locales/ja.yml @@ -9,6 +9,7 @@ chat: description: チームチャットメッセージのミュートを切り替えます mute-on: チームチャットをミュートしました. mute-off: チームチャットのミュートを解除しました. + reminder: (チームチャットがミュートされています - /teamchat mute でミュートを解除してください) spy: description: すべてのチームチャットのスパイを切り替えます spy-on: チームチャットスパイ オン. diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml index 0c5dbd7..796884c 100644 --- a/src/main/resources/locales/ko.yml +++ b/src/main/resources/locales/ko.yml @@ -8,6 +8,7 @@ chat: description: "팀 채팅 메시지 음소거 전환" mute-on: 팀 채팅이 음소거되었습니다. mute-off: 팀 채팅 음소거가 해제되었습니다. + reminder: (팀 채팅이 음소거되었습니다 - /teamchat mute 로 음소거를 해제하세요) spy: description: "모든 팀 채팅 감시 켜기/끄기 전환" spy-on: 팀 채팅 감시가 켜졌습니다. diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index 575393c..1a205fa 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -8,6 +8,7 @@ chat: description: "ieslēdz/izslēdz komandas tērzēšanas ziņojumu apklusināšanu" mute-on: Komandas tērzēšana apklusināta. mute-off: Komandas tērzēšanas apklusināšana atcelta. + reminder: (Komandas tērzēšana apklusināta - izmanto /teamchat mute, lai atceltu apklusināšanu) spy: description: "ieslēdz/izslēdz visas komandas tērzēšanas uzraudzību" spy-on: Komandas tērzēšanas uzraudzība ieslēgta. diff --git a/src/main/resources/locales/nl.yml b/src/main/resources/locales/nl.yml index 42fcf51..5258600 100644 --- a/src/main/resources/locales/nl.yml +++ b/src/main/resources/locales/nl.yml @@ -8,6 +8,7 @@ chat: description: "zet het dempen van teamchat berichten aan/uit" mute-on: Teamchat gedempt. mute-off: Teamchat niet meer gedempt. + reminder: (Teamchat is gedempt - gebruik /teamchat mute om te ontdempen) spy: description: "zet het bespioneren van alle teamchats aan/uit" spy-on: Teamchat spion aan. diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml index 038ea4a..f888656 100644 --- a/src/main/resources/locales/pl.yml +++ b/src/main/resources/locales/pl.yml @@ -8,6 +8,7 @@ chat: description: "włącza/wyłącza wyciszanie wiadomości czatu drużynowego" mute-on: Czat drużynowy wyciszony. mute-off: Czat drużynowy odciszony. + reminder: (Czat drużynowy jest wyciszony - użyj /teamchat mute, aby go odciszyć) spy: description: "włącza/wyłącza szpiegowanie wszystkich czatów drużynowych" spy-on: Szpiegowanie czatu drużynowego włączone. diff --git a/src/main/resources/locales/pt-BR.yml b/src/main/resources/locales/pt-BR.yml index b7ad454..0d6792a 100644 --- a/src/main/resources/locales/pt-BR.yml +++ b/src/main/resources/locales/pt-BR.yml @@ -8,6 +8,7 @@ chat: description: "ativa/desativa o silenciamento de mensagens do chat da equipe" mute-on: Chat da equipe silenciado. mute-off: Chat da equipe não está mais silenciado. + reminder: (Chat da equipe está silenciado - use /teamchat mute para dessilenciar) spy: description: "ativa/desativa a espionagem de todos os chats da equipe" spy-on: Espião de chat da equipe ativado. diff --git a/src/main/resources/locales/pt.yml b/src/main/resources/locales/pt.yml index 6d94470..5e97560 100644 --- a/src/main/resources/locales/pt.yml +++ b/src/main/resources/locales/pt.yml @@ -8,6 +8,7 @@ chat: description: "ativa/desativa o silenciamento de mensagens do chat da equipa" mute-on: Chat da equipa silenciado. mute-off: Chat da equipa não está mais silenciado. + reminder: (Chat da equipa está silenciado - use /teamchat mute para dessilenciar) spy: description: "ativa/desativa a espionagem de todos os chats da equipa" spy-on: Espião de chat da equipa ativado. diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml index a369060..842dce3 100644 --- a/src/main/resources/locales/ro.yml +++ b/src/main/resources/locales/ro.yml @@ -8,6 +8,7 @@ chat: description: "activează/dezactivează dezactivarea sunetului chat-ului de echipă" mute-on: Chat de echipă dezactivat sonor. mute-off: Chat de echipă reactivat sonor. + reminder: (Chat-ul de echipă este dezactivat sonor - foloseste /teamchat mute pentru reactivare sonoră) spy: description: "activează/dezactivează spionarea tuturor chat-urilor de echipă" spy-on: Spion chat de echipă activat. diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml index 7a1ab9f..a1022d9 100644 --- a/src/main/resources/locales/ru.yml +++ b/src/main/resources/locales/ru.yml @@ -13,6 +13,7 @@ chat: description: включает/отключает отключение звука командного чата mute-on: Командный чат заглушен. mute-off: Командный чат разглушен. + reminder: (Командный чат заглушен - используй /teamchat mute для разглушения) spy: description: переключает шпионаж на все командные чаты spy-on: Слежка за командным чатам включена. diff --git a/src/main/resources/locales/tr.yml b/src/main/resources/locales/tr.yml index b9785a5..56d2125 100644 --- a/src/main/resources/locales/tr.yml +++ b/src/main/resources/locales/tr.yml @@ -9,6 +9,7 @@ chat: description: "takım sohbeti mesajlarını susturmayı açar/kapatır" mute-on: Takım sohbeti susturuldu. mute-off: Takım sohbeti susturma kaldırıldı. + reminder: (Takım sohbeti susturuldu - susturma kaldırmak için /teamchat mute komutunu kullan) spy: description: "takım sohbetlerini görmeni sağlar" spy-on: Takım sohbetlerini artık görebiliyorsun. diff --git a/src/main/resources/locales/uk.yml b/src/main/resources/locales/uk.yml index e3072ad..1bc59ea 100644 --- a/src/main/resources/locales/uk.yml +++ b/src/main/resources/locales/uk.yml @@ -8,6 +8,7 @@ chat: description: "вмикає/вимикає ігнорування повідомлень командного чату" mute-on: Командний чат заглушено. mute-off: Командний чат розглушено. + reminder: (Командний чат заглушено - використовуй /teamchat mute для розглушення) spy: description: "вмикає/вимикає стеження за всіма командними чатами" spy-on: Стеження за командним чатом увімкнено. diff --git a/src/main/resources/locales/vi.yml b/src/main/resources/locales/vi.yml index 6bcbf78..b97c137 100644 --- a/src/main/resources/locales/vi.yml +++ b/src/main/resources/locales/vi.yml @@ -8,6 +8,7 @@ chat: description: bật/tắt tắt tiếng tin nhắn chat đội mute-on: Đã tắt tiếng chat đội. mute-off: Đã bỏ tắt tiếng chat đội. + reminder: (Chat đội đã bị tắt tiếng - sử dụng /teamchat mute để bỏ tắt tiếng) spy: description: bật/tắt giám sát chat đội spy-on: Đã bật giám sát chat đội. diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 8178307..c9f016a 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -8,6 +8,7 @@ chat: description: "切换静音团队聊天消息" mute-on: 团队聊天已静音. mute-off: 团队聊天已取消静音. + reminder: (团队聊天已静音 - 使用 /teamchat mute 来取消静音) spy: description: "切换偷听所有团队聊天" spy-on: 团队聊天偷听已开启. diff --git a/src/main/resources/locales/zh-HK.yml b/src/main/resources/locales/zh-HK.yml index bce420e..82d8522 100644 --- a/src/main/resources/locales/zh-HK.yml +++ b/src/main/resources/locales/zh-HK.yml @@ -8,6 +8,7 @@ chat: description: "切換靜音團隊聊天訊息" mute-on: 團隊聊天已靜音. mute-off: 團隊聊天已取消靜音. + reminder: (團隊聊天已靜音 - 使用 /teamchat mute 來取消靜音) spy: description: "切換監視所有團隊聊天" spy-on: 團隊聊天監視已開啟. From 857ea7c58937e7bb8e37146f5c3350d02ffcc754 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 13 Apr 2026 05:39:16 -0700 Subject: [PATCH 35/36] Fix ExecutionException handling and extend CI to master branch - Log async chat errors via addon.logError instead of rethrowing as RuntimeException - Add master to CI push/PR triggers so release PRs are validated Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 2 ++ src/main/java/world/bentobox/chat/listeners/ChatListener.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c73f4e4..550d89a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,9 +4,11 @@ on: push: branches: - develop + - master pull_request: branches: - develop + - master jobs: build-and-test: diff --git a/src/main/java/world/bentobox/chat/listeners/ChatListener.java b/src/main/java/world/bentobox/chat/listeners/ChatListener.java index 6da5457..1547591 100644 --- a/src/main/java/world/bentobox/chat/listeners/ChatListener.java +++ b/src/main/java/world/bentobox/chat/listeners/ChatListener.java @@ -121,7 +121,7 @@ public void onChat(final AsyncPlayerChatEvent e) { } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch (java.util.concurrent.ExecutionException ex) { - throw new RuntimeException("Failed to process async chat on the main thread", ex); + addon.logError("Failed to process async chat for " + p.getName() + ": " + ex.getCause()); } } else if (handleChatSync(p, message)) { e.setCancelled(true); From d410cb0fb2b4a802c8ce4390aae7ef7fdc362fd2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 13 Apr 2026 05:50:49 -0700 Subject: [PATCH 36/36] Fix case-insensitive world name matching in getWorldsFromExtra Config entries like "Spawn_World" now match server world names like "spawn_world". Adds ChatTest covering exact match, case mismatch, no match, unknown game mode, and multiple game mode scenarios. Co-Authored-By: Claude Sonnet 4.6 --- src/main/java/world/bentobox/chat/Chat.java | 2 +- .../java/world/bentobox/chat/ChatTest.java | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/test/java/world/bentobox/chat/ChatTest.java diff --git a/src/main/java/world/bentobox/chat/Chat.java b/src/main/java/world/bentobox/chat/Chat.java index 7f71650..0326453 100644 --- a/src/main/java/world/bentobox/chat/Chat.java +++ b/src/main/java/world/bentobox/chat/Chat.java @@ -115,7 +115,7 @@ public List getWorldsFromExtra(String worldName) { Map> extraWorlds = settings.getExtraChatWorlds(); List result = new ArrayList<>(); for (Map.Entry> entry : extraWorlds.entrySet()) { - if (entry.getValue().contains(worldName)) { + if (entry.getValue().stream().anyMatch(worldName::equalsIgnoreCase)) { getPlugin().getAddonsManager().getGameModeAddons().stream() .filter(gm -> entry.getKey().equalsIgnoreCase(gm.getDescription().getName())) .findFirst() diff --git a/src/test/java/world/bentobox/chat/ChatTest.java b/src/test/java/world/bentobox/chat/ChatTest.java new file mode 100644 index 0000000..ed87b48 --- /dev/null +++ b/src/test/java/world/bentobox/chat/ChatTest.java @@ -0,0 +1,105 @@ +package world.bentobox.chat; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; + +import org.bukkit.World; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; + +class ChatTest extends CommonTestSetup { + + private Chat chat; + private GameModeAddon bskyblock; + private World bskyblockOverWorld; + + @BeforeEach + @Override + public void setUp() throws Exception { + super.setUp(); + chat = WhiteBox.newUninitializedInstance(Chat.class); + WhiteBox.setInternalState(chat, "settings", settings); + + bskyblockOverWorld = mock(World.class); + bskyblock = mock(GameModeAddon.class); + AddonDescription desc = new AddonDescription.Builder("world.bentobox.bskyblock.BSkyBlock", "BSkyBlock", "1.0.0").build(); + when(bskyblock.getDescription()).thenReturn(desc); + when(bskyblock.getOverWorld()).thenReturn(bskyblockOverWorld); + when(am.getGameModeAddons()).thenReturn(List.of(bskyblock)); + } + + @Test + void getWorldsFromExtra_emptyConfig_returnsEmpty() { + when(settings.getExtraChatWorlds()).thenReturn(Map.of()); + + List result = chat.getWorldsFromExtra("any_world"); + + assertTrue(result.isEmpty()); + } + + @Test + void getWorldsFromExtra_exactMatch_returnsOverworld() { + when(settings.getExtraChatWorlds()).thenReturn(Map.of("BSkyBlock", List.of("spawn_world"))); + + List result = chat.getWorldsFromExtra("spawn_world"); + + assertEquals(List.of(bskyblockOverWorld), result); + } + + @Test + void getWorldsFromExtra_caseInsensitiveWorldName_returnsOverworld() { + // Config lists "Spawn_World" but server names it "spawn_world" + when(settings.getExtraChatWorlds()).thenReturn(Map.of("BSkyBlock", List.of("Spawn_World"))); + + List result = chat.getWorldsFromExtra("spawn_world"); + + assertEquals(List.of(bskyblockOverWorld), result); + } + + @Test + void getWorldsFromExtra_noMatchingWorld_returnsEmpty() { + when(settings.getExtraChatWorlds()).thenReturn(Map.of("BSkyBlock", List.of("spawn_world"))); + + List result = chat.getWorldsFromExtra("some_other_world"); + + assertTrue(result.isEmpty()); + } + + @Test + void getWorldsFromExtra_unknownGameMode_returnsEmpty() { + // Config references a game mode that isn't registered + when(settings.getExtraChatWorlds()).thenReturn(Map.of("UnknownMode", List.of("spawn_world"))); + + List result = chat.getWorldsFromExtra("spawn_world"); + + assertTrue(result.isEmpty()); + } + + @Test + void getWorldsFromExtra_multipleGameModes_returnsAllMatchingOverworlds() { + GameModeAddon acidisland = mock(GameModeAddon.class); + World acidOverWorld = mock(World.class); + AddonDescription acidDesc = new AddonDescription.Builder("world.bentobox.acidisland.AcidIsland", "AcidIsland", "1.0.0").build(); + when(acidisland.getDescription()).thenReturn(acidDesc); + when(acidisland.getOverWorld()).thenReturn(acidOverWorld); + when(am.getGameModeAddons()).thenReturn(List.of(bskyblock, acidisland)); + + when(settings.getExtraChatWorlds()).thenReturn(Map.of( + "BSkyBlock", List.of("spawn_world"), + "AcidIsland", List.of("spawn_world"))); + + List result = chat.getWorldsFromExtra("spawn_world"); + + assertEquals(2, result.size()); + assertTrue(result.contains(bskyblockOverWorld)); + assertTrue(result.contains(acidOverWorld)); + } +}