feat(pocket-base): add mob spawning toggle
Release / release (push) Successful in 2m46s
CI / build (push) Successful in 1m28s

This commit is contained in:
dmg
2026-08-24 09:57:48 -04:00
parent 11e3ad1720
commit 23e8fcec6e
13 changed files with 376 additions and 17 deletions
@@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -95,6 +96,16 @@ final class BaseSettingsCommandTest {
List.of("upgrade"),
command.onTabComplete(null, null, "basesettings", new String[] {"pocket", "u"})
);
assertEquals(
List.of("mobs"),
command.onTabComplete(null, null, "basesettings", new String[] {"pocket", "m"})
);
assertEquals(
List.of("enable"),
command.onTabComplete(
null, null, "basesettings", new String[] {"pocket", "mobs", "e"}
)
);
for (String setting : List.of("navigation", "flight", "border", "spawnable", "bossbar")) {
assertEquals(
List.of("disable"),
@@ -198,6 +209,101 @@ final class BaseSettingsCommandTest {
verify(inventory).setStorageContents(any(ItemStack[].class));
}
@Test
void pocketOwnerCanEnableNaturalMobSpawning() throws Exception {
UUID playerId = UUID.randomUUID();
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(playerId);
when(player.getName()).thenReturn("Builder");
BaseStateManager stateManager = mock(BaseStateManager.class);
when(stateManager.player(playerId, "Builder"))
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(pocketBases.state(playerId)).thenReturn(
new PocketBaseState(playerId, 1, java.util.Optional.empty(), false)
);
when(pocketBases.setMobSpawning(playerId, true)).thenReturn(
new PocketBaseState(playerId, 1, java.util.Optional.empty(), true)
);
BaseSettingsCommand command = new BaseSettingsCommand(
stateManager,
new PluginSettingsProvider(PluginSettings.from(Map.of())),
mock(BaseFlightController.class),
pocketBases
);
command.onCommand(
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
);
verify(pocketBases).setMobSpawning(playerId, true);
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
message -> message.contains("mob spawning") && message.contains("enabled")
));
}
@Test
void failedPocketMobSpawningChangeReportsOnlyFailure() throws Exception {
UUID playerId = UUID.randomUUID();
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(playerId);
when(player.getName()).thenReturn("Builder");
BaseStateManager stateManager = mock(BaseStateManager.class);
when(stateManager.player(playerId, "Builder"))
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(pocketBases.state(playerId)).thenReturn(
new PocketBaseState(playerId, 1, java.util.Optional.empty(), false)
);
when(pocketBases.setMobSpawning(playerId, true))
.thenThrow(new IOException("save failed"));
BaseSettingsCommand command = new BaseSettingsCommand(
stateManager,
new PluginSettingsProvider(PluginSettings.from(Map.of())),
mock(BaseFlightController.class),
pocketBases
);
command.onCommand(
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
);
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
message -> message.contains("could not be changed")
));
verify(player, never()).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
message -> message.contains("is now")
));
}
@Test
void lockedPlayerCannotEnablePocketMobSpawning() throws Exception {
UUID playerId = UUID.randomUUID();
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(playerId);
when(player.getName()).thenReturn("Builder");
BaseStateManager stateManager = mock(BaseStateManager.class);
when(stateManager.player(playerId, "Builder"))
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(pocketBases.state(playerId)).thenReturn(PocketBaseState.locked(playerId));
BaseSettingsCommand command = new BaseSettingsCommand(
stateManager,
new PluginSettingsProvider(PluginSettings.from(Map.of())),
mock(BaseFlightController.class),
pocketBases
);
command.onCommand(
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
);
verify(pocketBases, never()).setMobSpawning(playerId, true);
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
message -> message.contains("Pocket Base I") && message.contains("locked")
));
}
@Test
void failedPocketUpgradeRestoresPayment() throws Exception {
UUID playerId = UUID.randomUUID();
@@ -236,6 +342,34 @@ final class BaseSettingsCommandTest {
assertEquals(64, restored[0].getAmount());
}
@Test
void statusDisplaysPocketMobSpawningPreference() {
UUID playerId = UUID.randomUUID();
Player player = mock(Player.class);
when(player.getUniqueId()).thenReturn(playerId);
when(player.getName()).thenReturn("Builder");
BaseStateManager stateManager = mock(BaseStateManager.class);
when(stateManager.player(playerId, "Builder"))
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
when(pocketBases.state(playerId)).thenReturn(
new PocketBaseState(playerId, 1, java.util.Optional.empty(), false)
);
BaseSettingsCommand command = new BaseSettingsCommand(
stateManager,
new PluginSettingsProvider(PluginSettings.from(Map.of())),
mock(BaseFlightController.class),
pocketBases
);
command.onCommand(player, null, "basesettings", new String[] {"status"});
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
message -> message.contains("Pocket Base 1")
&& message.contains("mob spawning=disabled")
));
}
@Test
void bossbarEnableIsIdempotent() {
UUID playerId = UUID.randomUUID();
@@ -0,0 +1,66 @@
package games.dmg.spigotbase;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.UnaryOperator;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
final class PocketBaseManagerTest {
@Test
void persistsAndAppliesMobSpawningPreference() throws Exception {
UUID ownerId = UUID.randomUUID();
PocketBaseState current = new PocketBaseState(ownerId, 1, Optional.empty(), false);
PocketBaseStateManager states = mock(PocketBaseStateManager.class);
PocketBaseWorldService worlds = mock(PocketBaseWorldService.class);
when(states.state(ownerId)).thenReturn(current);
when(states.updateAndSave(org.mockito.ArgumentMatchers.eq(ownerId),
org.mockito.ArgumentMatchers.any())).thenAnswer(invocation -> {
@SuppressWarnings("unchecked")
UnaryOperator<PocketBaseState> operation = invocation.getArgument(1);
return operation.apply(current);
});
PocketBaseManager manager = new PocketBaseManager(
states,
worlds,
new PluginSettingsProvider(PluginSettings.from(Map.of()))
);
manager.setMobSpawning(ownerId, true);
InOrder order = inOrder(worlds, states);
order.verify(worlds).setMobSpawning(ownerId, true);
order.verify(states).updateAndSave(
org.mockito.ArgumentMatchers.eq(ownerId), org.mockito.ArgumentMatchers.any()
);
}
@Test
void restoresWorldSettingWhenPersistenceFails() throws Exception {
UUID ownerId = UUID.randomUUID();
PocketBaseState current = new PocketBaseState(ownerId, 1, Optional.empty(), false);
PocketBaseStateManager states = mock(PocketBaseStateManager.class);
PocketBaseWorldService worlds = mock(PocketBaseWorldService.class);
when(states.state(ownerId)).thenReturn(current);
when(states.updateAndSave(org.mockito.ArgumentMatchers.eq(ownerId),
org.mockito.ArgumentMatchers.any())).thenThrow(new IOException("save failed"));
PocketBaseManager manager = new PocketBaseManager(
states,
worlds,
new PluginSettingsProvider(PluginSettings.from(Map.of()))
);
assertThrows(IOException.class, () -> manager.setMobSpawning(ownerId, true));
verify(worlds).setMobSpawning(ownerId, true);
verify(worlds).setMobSpawning(ownerId, false);
}
}
@@ -9,6 +9,7 @@ import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.Server;
@@ -41,9 +42,30 @@ final class PocketBaseWorldServiceTest {
verify(blocks.get(new BlockPosition(-2, 65, 0)))
.setType(Material.DIAMOND_BLOCK, false);
verify(world).setSpawnLocation(0, 65, 5);
verify(world).setSpawnFlags(false, false);
assertEquals(ownerId, service.ownerForWorld(worldId).orElseThrow());
}
@Test
void reappliesSavedMobSpawningPreferenceWhenWorldLoads() {
UUID ownerId = UUID.randomUUID();
Server server = mock(Server.class);
World world = mock(World.class);
when(server.getWorld(org.mockito.ArgumentMatchers.anyString())).thenReturn(world);
when(world.getUID()).thenReturn(UUID.randomUUID());
PocketBaseWorldService service = new PocketBaseWorldService(
server,
new PluginSettingsProvider(PluginSettings.from(Map.of()))
);
service.loadExisting(Map.of(
ownerId,
new PocketBaseState(ownerId, 1, Optional.empty(), true)
));
verify(world).setSpawnFlags(true, true);
}
@Test
void laterLevelAddsRingWithoutOverwritingExistingTerrain() {
UUID ownerId = UUID.randomUUID();
@@ -1,7 +1,9 @@
package games.dmg.spigotbase;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
@@ -22,7 +24,8 @@ final class YamlPocketBaseRepositoryTest {
7,
Optional.of(new PocketPortalLocation(
worldId, "world", 10, 65, -4, PocketPortalAxis.X
))
)),
true
);
YamlPocketBaseRepository repository = new YamlPocketBaseRepository(
temporaryDirectory.resolve("pocket-bases.yml")
@@ -32,4 +35,14 @@ final class YamlPocketBaseRepositoryTest {
assertEquals(expected, repository.load().get(ownerId));
}
@Test
void existingRecordWithoutMobPreferenceDefaultsToDisabled() throws Exception {
UUID ownerId = UUID.randomUUID();
Path stateFile = temporaryDirectory.resolve("pocket-bases.yml");
Files.writeString(stateFile, "owners:\n " + ownerId + ":\n level: 1\n");
YamlPocketBaseRepository repository = new YamlPocketBaseRepository(stateFile);
assertFalse(repository.load().get(ownerId).mobSpawningEnabled());
}
}