feat(pocket-base): split hostile and passive mob spawning
This commit is contained in:
@@ -29,7 +29,7 @@ The plugin JAR is written to `build/libs/`.
|
||||
/basesettings status
|
||||
/basesettings upgrade
|
||||
/basesettings pocket upgrade
|
||||
/basesettings pocket mobs <enable|disable>
|
||||
/basesettings pocket mobs <hostile|passive> <enable|disable>
|
||||
/basesettings pocket type <void|nether|overworld> <subtype>
|
||||
/basesettings visitors <allowed|blocked>
|
||||
/basesettings navigation <enable|disable>
|
||||
@@ -45,7 +45,7 @@ After 250 Survival-mode block placements anywhere by default, the spawnable over
|
||||
|
||||
`/base` has a stationary warm-up. Looking around is allowed, while movement between blocks, damage, teleportation, world changes, death, logout, and conflicting teleport commands cancel it without consuming the cooldown.
|
||||
|
||||
Base IV owners can purchase and expand a persistent Pocket Base with `/basesettings pocket upgrade`. Activating a complete diamond-block portal frame inside the normal base with flint and steel opens one public entrance to the owner's grass platform in a private void world. The owner can move the return portal by building and igniting another complete frame inside the unlocked Pocket Base boundary; only the newly activated return portal remains functional. Natural hostile and passive mob spawning is disabled by default and can be controlled by the owner with `/basesettings pocket mobs <enable|disable>`.
|
||||
Base IV owners can purchase and expand a persistent Pocket Base with `/basesettings pocket upgrade`. Activating a complete diamond-block portal frame inside the normal base with flint and steel opens one public entrance to the owner's grass platform in a private void world. The owner can move the return portal by building and igniting another complete frame inside the unlocked Pocket Base boundary; only the newly activated return portal remains functional. Natural hostile and passive mob spawning is disabled by default. Owners can control each category independently with `/basesettings pocket mobs <hostile|passive> <enable|disable>`.
|
||||
|
||||
Owners can change biome metadata without altering Pocket Base blocks, entities, inventories, or portals by using `/basesettings pocket type <void|nether|overworld> <subtype>`. Void uses `the_void`; Nether and Overworld accept their compatible vanilla biome names, such as `crimson_forest` or `plains`. Each change defaults to 16 netherite blocks, is configurable, and affects applicable natural mob selection when spawning is enabled. The Pocket Base remains a void-generated normal-environment world; dimension-specific mechanics do not change.
|
||||
|
||||
|
||||
+3
-2
@@ -95,8 +95,9 @@ description: Chronological record of material decisions affecting the Spigot Bas
|
||||
## 2026-08-24 — Pocket Base mob-spawning control
|
||||
|
||||
- Disabled natural hostile and passive mob spawning by default in new and existing Pocket Bases without a saved preference.
|
||||
- Added the persisted owner setting `/basesettings pocket mobs enable|disable`, contextual autocomplete, and status reporting.
|
||||
- Reapplied saved spawn flags when Pocket Base worlds load and restored the prior world setting when persistence fails.
|
||||
- Added independent persisted owner settings through `/basesettings pocket mobs <hostile|passive> <enable|disable>`, contextual autocomplete, and status reporting.
|
||||
- Migrated each legacy combined mob-spawning preference to both categories to preserve existing behavior.
|
||||
- Reapplied both saved spawn flags when Pocket Base worlds load and restored both prior world settings when persistence fails.
|
||||
- Verified the feature with `./gradlew clean check jar`.
|
||||
|
||||
## 2026-08-24 — Relocatable Pocket Base return portals
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-018: Control Pocket Base mob spawning"
|
||||
description: Let Pocket Base owners control natural mob spawning in their private world, with spawning disabled by default.
|
||||
description: Let Pocket Base owners control hostile and passive natural mob spawning independently in their private world, with both disabled by default.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-018: Control Pocket Base mob spawning
|
||||
|
||||
As a **Pocket Base owner**, I want to enable or disable natural mob spawning in my Pocket Base so that I can choose whether the private world supports normal mob activity.
|
||||
As a **Pocket Base owner**, I want to enable or disable hostile and passive natural mob spawning independently in my Pocket Base so that I can choose which kinds of normal mob activity its private world supports.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] Natural mob spawning is disabled by default in every new Pocket Base and in existing Pocket Bases that have no saved preference.
|
||||
- [x] A Pocket Base owner can use `/basesettings pocket mobs enable|disable` to control natural mob spawning in their own Pocket Base.
|
||||
- [x] The setting controls both hostile and passive natural spawning only in the owner's Pocket Base.
|
||||
- [x] The command is unavailable until Pocket Base I is unlocked.
|
||||
- [x] The command and its modes are offered through contextual autocomplete.
|
||||
- [x] `/basesettings status` displays the current Pocket Base mob-spawning setting.
|
||||
- [x] The preference persists across restarts and is reapplied when the Pocket Base world loads.
|
||||
- [x] Disabling natural spawning does not remove existing mobs or prevent explicitly spawned or summoned mobs.
|
||||
- [x] Persistence or world-application failures produce a clear failure message without reporting a successful change.
|
||||
- [x] Hostile and passive natural mob spawning are both disabled by default in every new Pocket Base and in existing Pocket Bases that have no saved preference.
|
||||
- [x] A Pocket Base owner can use `/basesettings pocket mobs hostile enable|disable` to control hostile natural mob spawning in their own Pocket Base.
|
||||
- [x] A Pocket Base owner can use `/basesettings pocket mobs passive enable|disable` to control passive natural mob spawning in their own Pocket Base.
|
||||
- [x] Changing one mob category does not change the other category.
|
||||
- [x] The commands are unavailable until Pocket Base I is unlocked.
|
||||
- [x] The commands, categories, and modes are offered through contextual autocomplete.
|
||||
- [x] `/basesettings status` displays the current hostile and passive Pocket Base mob-spawning settings.
|
||||
- [x] Both preferences persist across restarts and are reapplied when the Pocket Base world loads.
|
||||
- [x] Existing saved `mob-spawning-enabled` values migrate to both category preferences, preserving prior behavior.
|
||||
- [x] Disabling either natural-spawning category does not remove existing mobs or prevent explicitly spawned or summoned mobs.
|
||||
- [x] Persistence or world-application failures produce a clear failure message, restore both prior world flags, and do not report a successful change.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
&& arguments[1].equalsIgnoreCase("upgrade")) {
|
||||
return purchasePocketUpgrade(player, state);
|
||||
}
|
||||
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")
|
||||
&& arguments[1].equalsIgnoreCase("mobs")) {
|
||||
return updatePocketMobSpawning(player, arguments[2]);
|
||||
return updatePocketMobSpawning(player, arguments[2], arguments[3]);
|
||||
}
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")
|
||||
&& arguments[1].equalsIgnoreCase("type")) {
|
||||
@@ -240,7 +240,11 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updatePocketMobSpawning(Player player, String mode) {
|
||||
private boolean updatePocketMobSpawning(
|
||||
Player player,
|
||||
String category,
|
||||
String mode
|
||||
) {
|
||||
if (pocketBases == null) {
|
||||
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
|
||||
return true;
|
||||
@@ -256,17 +260,30 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
return true;
|
||||
}
|
||||
final PocketBaseState updated;
|
||||
final boolean settingEnabled;
|
||||
try {
|
||||
updated = pocketBases.setMobSpawning(player.getUniqueId(), enabled);
|
||||
if (category.equalsIgnoreCase("hostile")) {
|
||||
updated = pocketBases.setHostileMobSpawning(
|
||||
player.getUniqueId(), enabled
|
||||
);
|
||||
settingEnabled = updated.hostileMobSpawningEnabled();
|
||||
} else if (category.equalsIgnoreCase("passive")) {
|
||||
updated = pocketBases.setPassiveMobSpawning(
|
||||
player.getUniqueId(), enabled
|
||||
);
|
||||
settingEnabled = updated.passiveMobSpawningEnabled();
|
||||
} else {
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
player.sendMessage(ChatColor.RED
|
||||
+ "The Pocket Base mob-spawning setting could not be changed.");
|
||||
return true;
|
||||
}
|
||||
player.sendMessage(ChatColor.YELLOW + "Pocket Base mob spawning is now "
|
||||
+ (updated.mobSpawningEnabled()
|
||||
? ChatColor.GREEN + "enabled"
|
||||
: ChatColor.RED + "disabled")
|
||||
player.sendMessage(ChatColor.YELLOW + "Pocket Base "
|
||||
+ category.toLowerCase(Locale.ROOT) + " mob spawning is now "
|
||||
+ (settingEnabled ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
@@ -514,8 +531,10 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
+ settings.current().pocketBaseCurrencyMaterial().toLowerCase(Locale.ROOT)
|
||||
+ " → /basesettings pocket upgrade; type="
|
||||
+ pocket.biome().worldType().commandName() + "/" + pocket.biome().commandName()
|
||||
+ "; mob spawning="
|
||||
+ (pocket.mobSpawningEnabled() ? "enabled" : "disabled"));
|
||||
+ "; hostile spawning="
|
||||
+ (pocket.hostileMobSpawningEnabled() ? "enabled" : "disabled")
|
||||
+ "; passive spawning="
|
||||
+ (pocket.passiveMobSpawningEnabled() ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
private void showCooldownPath(Player player, PlayerState state) {
|
||||
@@ -557,7 +576,9 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")) {
|
||||
String prefix = arguments[2].toLowerCase(Locale.ROOT);
|
||||
if (arguments[1].equalsIgnoreCase("mobs")) {
|
||||
return ENABLE_MODES.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
||||
return List.of("hostile", "passive").stream()
|
||||
.filter(category -> category.startsWith(prefix))
|
||||
.toList();
|
||||
}
|
||||
if (arguments[1].equalsIgnoreCase("type")) {
|
||||
return POCKET_WORLD_TYPES.stream()
|
||||
@@ -565,9 +586,16 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")
|
||||
&& arguments[1].equalsIgnoreCase("type")) {
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")) {
|
||||
String prefix = arguments[3].toLowerCase(Locale.ROOT);
|
||||
if (arguments[1].equalsIgnoreCase("mobs")
|
||||
&& (arguments[2].equalsIgnoreCase("hostile")
|
||||
|| arguments[2].equalsIgnoreCase("passive"))) {
|
||||
return ENABLE_MODES.stream()
|
||||
.filter(mode -> mode.startsWith(prefix))
|
||||
.toList();
|
||||
}
|
||||
if (arguments[1].equalsIgnoreCase("type")) {
|
||||
return PocketBaseWorldType.fromCommand(arguments[2])
|
||||
.map(PocketBaseBiome::commandNames)
|
||||
.orElse(List.of())
|
||||
@@ -575,6 +603,7 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
.filter(biome -> biome.startsWith(prefix))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@@ -634,7 +663,8 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private static void sendUsage(Player player) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /basesettings "
|
||||
+ "[status|upgrade|pocket upgrade|pocket mobs <enable|disable>"
|
||||
+ "[status|upgrade|pocket upgrade|pocket mobs "
|
||||
+ "<hostile|passive> <enable|disable>"
|
||||
+ "|pocket type <void|nether|overworld> <subtype>"
|
||||
+ "|visitors <allowed|blocked>|navigation <enable|disable>"
|
||||
+ "|flight <enable|disable>|border <enable|disable>"
|
||||
|
||||
@@ -92,20 +92,48 @@ final class PocketBaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
PocketBaseState setMobSpawning(UUID ownerId, boolean enabled) throws IOException {
|
||||
PocketBaseState setHostileMobSpawning(UUID ownerId, boolean enabled)
|
||||
throws IOException {
|
||||
PocketBaseState current = states.state(ownerId);
|
||||
return setMobSpawning(
|
||||
current,
|
||||
enabled,
|
||||
current.passiveMobSpawningEnabled(),
|
||||
state -> state.withHostileMobSpawningEnabled(enabled)
|
||||
);
|
||||
}
|
||||
|
||||
PocketBaseState setPassiveMobSpawning(UUID ownerId, boolean enabled)
|
||||
throws IOException {
|
||||
PocketBaseState current = states.state(ownerId);
|
||||
return setMobSpawning(
|
||||
current,
|
||||
current.hostileMobSpawningEnabled(),
|
||||
enabled,
|
||||
state -> state.withPassiveMobSpawningEnabled(enabled)
|
||||
);
|
||||
}
|
||||
|
||||
private PocketBaseState setMobSpawning(
|
||||
PocketBaseState current,
|
||||
boolean hostileEnabled,
|
||||
boolean passiveEnabled,
|
||||
java.util.function.UnaryOperator<PocketBaseState> update
|
||||
) throws IOException {
|
||||
if (current.level() < 1) {
|
||||
throw new IllegalStateException("Pocket Base I is still locked");
|
||||
}
|
||||
worlds.setMobSpawning(ownerId, enabled);
|
||||
UUID ownerId = current.ownerId();
|
||||
worlds.setMobSpawning(ownerId, hostileEnabled, passiveEnabled);
|
||||
try {
|
||||
return states.updateAndSave(
|
||||
ownerId,
|
||||
state -> state.withMobSpawningEnabled(enabled)
|
||||
);
|
||||
return states.updateAndSave(ownerId, update);
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
try {
|
||||
worlds.setMobSpawning(ownerId, current.mobSpawningEnabled());
|
||||
worlds.setMobSpawning(
|
||||
ownerId,
|
||||
current.hostileMobSpawningEnabled(),
|
||||
current.passiveMobSpawningEnabled()
|
||||
);
|
||||
} catch (RuntimeException rollbackFailure) {
|
||||
exception.addSuppressed(rollbackFailure);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ public record PocketBaseState(
|
||||
int level,
|
||||
Optional<PocketPortalLocation> entrance,
|
||||
Optional<PocketPortalLocation> returnPortal,
|
||||
boolean mobSpawningEnabled,
|
||||
boolean hostileMobSpawningEnabled,
|
||||
boolean passiveMobSpawningEnabled,
|
||||
PocketBaseBiome biome
|
||||
) {
|
||||
public PocketBaseState {
|
||||
@@ -32,73 +33,64 @@ public record PocketBaseState(
|
||||
Optional<PocketPortalLocation> entrance
|
||||
) {
|
||||
this(
|
||||
ownerId, level, entrance, Optional.empty(), false, PocketBaseBiome.THE_VOID
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState(
|
||||
UUID ownerId,
|
||||
int level,
|
||||
Optional<PocketPortalLocation> entrance,
|
||||
boolean mobSpawningEnabled
|
||||
) {
|
||||
this(
|
||||
ownerId, level, entrance, Optional.empty(), mobSpawningEnabled,
|
||||
PocketBaseBiome.THE_VOID
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState(
|
||||
UUID ownerId,
|
||||
int level,
|
||||
Optional<PocketPortalLocation> entrance,
|
||||
Optional<PocketPortalLocation> returnPortal,
|
||||
boolean mobSpawningEnabled
|
||||
) {
|
||||
this(
|
||||
ownerId, level, entrance, returnPortal, mobSpawningEnabled,
|
||||
ownerId, level, entrance, Optional.empty(), false, false,
|
||||
PocketBaseBiome.THE_VOID
|
||||
);
|
||||
}
|
||||
|
||||
public static PocketBaseState locked(UUID ownerId) {
|
||||
return new PocketBaseState(
|
||||
ownerId, 0, Optional.empty(), Optional.empty(), false,
|
||||
ownerId, 0, Optional.empty(), Optional.empty(), false, false,
|
||||
PocketBaseBiome.THE_VOID
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withLevel(int newLevel) {
|
||||
return new PocketBaseState(
|
||||
ownerId, newLevel, entrance, returnPortal, mobSpawningEnabled, biome
|
||||
ownerId, newLevel, entrance, returnPortal, hostileMobSpawningEnabled,
|
||||
passiveMobSpawningEnabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withEntrance(PocketPortalLocation portal) {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, Optional.of(portal), returnPortal, mobSpawningEnabled, biome
|
||||
ownerId, level, Optional.of(portal), returnPortal,
|
||||
hostileMobSpawningEnabled, passiveMobSpawningEnabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withoutEntrance() {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, Optional.empty(), returnPortal, mobSpawningEnabled, biome
|
||||
ownerId, level, Optional.empty(), returnPortal, hostileMobSpawningEnabled,
|
||||
passiveMobSpawningEnabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withReturnPortal(PocketPortalLocation portal) {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, entrance, Optional.of(portal), mobSpawningEnabled, biome
|
||||
ownerId, level, entrance, Optional.of(portal), hostileMobSpawningEnabled,
|
||||
passiveMobSpawningEnabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withMobSpawningEnabled(boolean enabled) {
|
||||
return new PocketBaseState(ownerId, level, entrance, returnPortal, enabled, biome);
|
||||
public PocketBaseState withHostileMobSpawningEnabled(boolean enabled) {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, entrance, returnPortal, enabled,
|
||||
passiveMobSpawningEnabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withPassiveMobSpawningEnabled(boolean enabled) {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, entrance, returnPortal, hostileMobSpawningEnabled,
|
||||
enabled, biome
|
||||
);
|
||||
}
|
||||
|
||||
public PocketBaseState withBiome(PocketBaseBiome selectedBiome) {
|
||||
return new PocketBaseState(
|
||||
ownerId, level, entrance, returnPortal, mobSpawningEnabled, selectedBiome
|
||||
ownerId, level, entrance, returnPortal, hostileMobSpawningEnabled,
|
||||
passiveMobSpawningEnabled, selectedBiome
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,11 @@ final class PocketBaseWorldService {
|
||||
for (PocketBaseState state : states.values()) {
|
||||
if (state.level() > 0) {
|
||||
setBiome(state.ownerId(), state.level(), state.biome());
|
||||
setMobSpawning(state.ownerId(), state.mobSpawningEnabled());
|
||||
setMobSpawning(
|
||||
state.ownerId(),
|
||||
state.hostileMobSpawningEnabled(),
|
||||
state.passiveMobSpawningEnabled()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,8 +95,12 @@ final class PocketBaseWorldService {
|
||||
return ensureWorld(ownerId);
|
||||
}
|
||||
|
||||
void setMobSpawning(UUID ownerId, boolean enabled) {
|
||||
ensureWorld(ownerId).setSpawnFlags(enabled, enabled);
|
||||
void setMobSpawning(
|
||||
UUID ownerId,
|
||||
boolean hostileEnabled,
|
||||
boolean passiveEnabled
|
||||
) {
|
||||
ensureWorld(ownerId).setSpawnFlags(hostileEnabled, passiveEnabled);
|
||||
}
|
||||
|
||||
void setBiome(UUID ownerId, int level, PocketBaseBiome biome) {
|
||||
|
||||
@@ -44,7 +44,8 @@ public final class YamlPocketBaseRepository {
|
||||
level,
|
||||
loadPortal(yaml, path + ".entrance"),
|
||||
loadPortal(yaml, path + ".return-portal"),
|
||||
yaml.getBoolean(path + ".mob-spawning-enabled", false),
|
||||
loadMobPreference(yaml, path, "hostile-mob-spawning-enabled"),
|
||||
loadMobPreference(yaml, path, "passive-mob-spawning-enabled"),
|
||||
loadBiome(yaml, path)
|
||||
);
|
||||
states.put(ownerId, state);
|
||||
@@ -64,7 +65,14 @@ public final class YamlPocketBaseRepository {
|
||||
for (PocketBaseState state : states.values()) {
|
||||
String path = "owners." + state.ownerId();
|
||||
yaml.set(path + ".level", state.level());
|
||||
yaml.set(path + ".mob-spawning-enabled", state.mobSpawningEnabled());
|
||||
yaml.set(
|
||||
path + ".hostile-mob-spawning-enabled",
|
||||
state.hostileMobSpawningEnabled()
|
||||
);
|
||||
yaml.set(
|
||||
path + ".passive-mob-spawning-enabled",
|
||||
state.passiveMobSpawningEnabled()
|
||||
);
|
||||
yaml.set(path + ".world-type", state.biome().worldType().commandName());
|
||||
yaml.set(path + ".biome", state.biome().commandName());
|
||||
state.entrance().ifPresent(portal -> savePortal(yaml, path + ".entrance", portal));
|
||||
@@ -90,6 +98,17 @@ public final class YamlPocketBaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean loadMobPreference(
|
||||
YamlConfiguration yaml,
|
||||
String path,
|
||||
String categoryKey
|
||||
) {
|
||||
return yaml.getBoolean(
|
||||
path + "." + categoryKey,
|
||||
yaml.getBoolean(path + ".mob-spawning-enabled", false)
|
||||
);
|
||||
}
|
||||
|
||||
private static PocketBaseBiome loadBiome(YamlConfiguration yaml, String path) {
|
||||
String worldTypeName = yaml.getString(path + ".world-type");
|
||||
String biomeName = yaml.getString(path + ".biome");
|
||||
|
||||
@@ -119,10 +119,23 @@ final class BaseSettingsCommandTest {
|
||||
new String[] {"pocket", "type", "nether", "c"}
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
List.of("hostile"),
|
||||
command.onTabComplete(
|
||||
null, null, "basesettings", new String[] {"pocket", "mobs", "h"}
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
List.of("passive"),
|
||||
command.onTabComplete(
|
||||
null, null, "basesettings", new String[] {"pocket", "mobs", "p"}
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
List.of("enable"),
|
||||
command.onTabComplete(
|
||||
null, null, "basesettings", new String[] {"pocket", "mobs", "e"}
|
||||
null, null, "basesettings",
|
||||
new String[] {"pocket", "mobs", "hostile", "e"}
|
||||
)
|
||||
);
|
||||
for (String setting : List.of("navigation", "flight", "border", "spawnable", "bossbar")) {
|
||||
@@ -296,7 +309,7 @@ final class BaseSettingsCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void pocketOwnerCanEnableNaturalMobSpawning() throws Exception {
|
||||
void pocketOwnerCanEnableHostileMobSpawningWithoutChangingPassive() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
@@ -305,11 +318,13 @@ final class BaseSettingsCommandTest {
|
||||
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)
|
||||
PocketBaseState current = new PocketBaseState(
|
||||
playerId, 1, java.util.Optional.empty(), java.util.Optional.empty(),
|
||||
false, true, PocketBaseBiome.THE_VOID
|
||||
);
|
||||
when(pocketBases.setMobSpawning(playerId, true)).thenReturn(
|
||||
new PocketBaseState(playerId, 1, java.util.Optional.empty(), true)
|
||||
when(pocketBases.state(playerId)).thenReturn(current);
|
||||
when(pocketBases.setHostileMobSpawning(playerId, true)).thenReturn(
|
||||
current.withHostileMobSpawningEnabled(true)
|
||||
);
|
||||
BaseSettingsCommand command = new BaseSettingsCommand(
|
||||
stateManager,
|
||||
@@ -318,13 +333,14 @@ final class BaseSettingsCommandTest {
|
||||
pocketBases
|
||||
);
|
||||
|
||||
command.onCommand(
|
||||
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
|
||||
);
|
||||
command.onCommand(player, null, "basesettings", new String[] {
|
||||
"pocket", "mobs", "hostile", "enable"
|
||||
});
|
||||
|
||||
verify(pocketBases).setMobSpawning(playerId, true);
|
||||
verify(pocketBases).setHostileMobSpawning(playerId, true);
|
||||
verify(pocketBases, never()).setPassiveMobSpawning(playerId, true);
|
||||
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
|
||||
message -> message.contains("mob spawning") && message.contains("enabled")
|
||||
message -> message.contains("hostile mob spawning") && message.contains("enabled")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -339,9 +355,9 @@ final class BaseSettingsCommandTest {
|
||||
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
|
||||
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
||||
when(pocketBases.state(playerId)).thenReturn(
|
||||
new PocketBaseState(playerId, 1, java.util.Optional.empty(), false)
|
||||
new PocketBaseState(playerId, 1, java.util.Optional.empty())
|
||||
);
|
||||
when(pocketBases.setMobSpawning(playerId, true))
|
||||
when(pocketBases.setPassiveMobSpawning(playerId, true))
|
||||
.thenThrow(new IOException("save failed"));
|
||||
BaseSettingsCommand command = new BaseSettingsCommand(
|
||||
stateManager,
|
||||
@@ -350,9 +366,9 @@ final class BaseSettingsCommandTest {
|
||||
pocketBases
|
||||
);
|
||||
|
||||
command.onCommand(
|
||||
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
|
||||
);
|
||||
command.onCommand(player, null, "basesettings", new String[] {
|
||||
"pocket", "mobs", "passive", "enable"
|
||||
});
|
||||
|
||||
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
|
||||
message -> message.contains("could not be changed")
|
||||
@@ -380,11 +396,11 @@ final class BaseSettingsCommandTest {
|
||||
pocketBases
|
||||
);
|
||||
|
||||
command.onCommand(
|
||||
player, null, "basesettings", new String[] {"pocket", "mobs", "enable"}
|
||||
);
|
||||
command.onCommand(player, null, "basesettings", new String[] {
|
||||
"pocket", "mobs", "hostile", "enable"
|
||||
});
|
||||
|
||||
verify(pocketBases, never()).setMobSpawning(playerId, true);
|
||||
verify(pocketBases, never()).setHostileMobSpawning(playerId, true);
|
||||
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
|
||||
message -> message.contains("Pocket Base I") && message.contains("locked")
|
||||
));
|
||||
@@ -429,7 +445,7 @@ final class BaseSettingsCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void statusDisplaysPocketMobSpawningPreference() {
|
||||
void statusDisplaysPocketMobSpawningPreferences() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
@@ -439,7 +455,10 @@ final class BaseSettingsCommandTest {
|
||||
.thenReturn(PlayerState.newPlayer(playerId, "Builder"));
|
||||
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
||||
when(pocketBases.state(playerId)).thenReturn(
|
||||
new PocketBaseState(playerId, 1, java.util.Optional.empty(), false)
|
||||
new PocketBaseState(
|
||||
playerId, 1, java.util.Optional.empty(), java.util.Optional.empty(),
|
||||
true, false, PocketBaseBiome.THE_VOID
|
||||
)
|
||||
);
|
||||
BaseSettingsCommand command = new BaseSettingsCommand(
|
||||
stateManager,
|
||||
@@ -453,7 +472,8 @@ final class BaseSettingsCommandTest {
|
||||
verify(player).sendMessage(org.mockito.ArgumentMatchers.<String>argThat(
|
||||
message -> message.contains("Pocket Base 1")
|
||||
&& message.contains("type=void/the_void")
|
||||
&& message.contains("mob spawning=disabled")
|
||||
&& message.contains("hostile spawning=enabled")
|
||||
&& message.contains("passive spawning=disabled")
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ final class PocketBaseManagerTest {
|
||||
PocketBaseStateManager states = mock(PocketBaseStateManager.class);
|
||||
PocketBaseWorldService worlds = mock(PocketBaseWorldService.class);
|
||||
when(states.state(ownerId)).thenReturn(new PocketBaseState(
|
||||
ownerId, 1, Optional.empty(), Optional.of(relocated), false
|
||||
ownerId, 1, Optional.empty(), Optional.of(relocated), false, false,
|
||||
PocketBaseBiome.THE_VOID
|
||||
));
|
||||
PocketBaseManager manager = new PocketBaseManager(
|
||||
states,
|
||||
@@ -58,9 +59,12 @@ final class PocketBaseManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistsAndAppliesMobSpawningPreference() throws Exception {
|
||||
void persistsAndAppliesHostileMobSpawningWithoutChangingPassive() throws Exception {
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
PocketBaseState current = new PocketBaseState(ownerId, 1, Optional.empty(), false);
|
||||
PocketBaseState current = new PocketBaseState(
|
||||
ownerId, 1, Optional.empty(), Optional.empty(), false, true,
|
||||
PocketBaseBiome.THE_VOID
|
||||
);
|
||||
PocketBaseStateManager states = mock(PocketBaseStateManager.class);
|
||||
PocketBaseWorldService worlds = mock(PocketBaseWorldService.class);
|
||||
when(states.state(ownerId)).thenReturn(current);
|
||||
@@ -76,10 +80,12 @@ final class PocketBaseManagerTest {
|
||||
new PluginSettingsProvider(PluginSettings.from(Map.of()))
|
||||
);
|
||||
|
||||
manager.setMobSpawning(ownerId, true);
|
||||
PocketBaseState updated = manager.setHostileMobSpawning(ownerId, true);
|
||||
|
||||
assertEquals(true, updated.hostileMobSpawningEnabled());
|
||||
assertEquals(true, updated.passiveMobSpawningEnabled());
|
||||
InOrder order = inOrder(worlds, states);
|
||||
order.verify(worlds).setMobSpawning(ownerId, true);
|
||||
order.verify(worlds).setMobSpawning(ownerId, true, true);
|
||||
order.verify(states).updateAndSave(
|
||||
org.mockito.ArgumentMatchers.eq(ownerId), org.mockito.ArgumentMatchers.any()
|
||||
);
|
||||
@@ -137,9 +143,12 @@ final class PocketBaseManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void restoresWorldSettingWhenPersistenceFails() throws Exception {
|
||||
void restoresBothWorldSettingsWhenPersistenceFails() throws Exception {
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
PocketBaseState current = new PocketBaseState(ownerId, 1, Optional.empty(), false);
|
||||
PocketBaseState current = new PocketBaseState(
|
||||
ownerId, 1, Optional.empty(), Optional.empty(), true, false,
|
||||
PocketBaseBiome.THE_VOID
|
||||
);
|
||||
PocketBaseStateManager states = mock(PocketBaseStateManager.class);
|
||||
PocketBaseWorldService worlds = mock(PocketBaseWorldService.class);
|
||||
when(states.state(ownerId)).thenReturn(current);
|
||||
@@ -151,9 +160,10 @@ final class PocketBaseManagerTest {
|
||||
new PluginSettingsProvider(PluginSettings.from(Map.of()))
|
||||
);
|
||||
|
||||
assertThrows(IOException.class, () -> manager.setMobSpawning(ownerId, true));
|
||||
assertThrows(IOException.class,
|
||||
() -> manager.setPassiveMobSpawning(ownerId, true));
|
||||
|
||||
verify(worlds).setMobSpawning(ownerId, true);
|
||||
verify(worlds).setMobSpawning(ownerId, false);
|
||||
verify(worlds).setMobSpawning(ownerId, true, true);
|
||||
verify(worlds).setMobSpawning(ownerId, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,10 +93,13 @@ final class PocketBaseWorldServiceTest {
|
||||
|
||||
service.loadExisting(Map.of(
|
||||
ownerId,
|
||||
new PocketBaseState(ownerId, 1, Optional.empty(), true)
|
||||
new PocketBaseState(
|
||||
ownerId, 1, Optional.empty(), Optional.empty(), true, false,
|
||||
PocketBaseBiome.THE_VOID
|
||||
)
|
||||
));
|
||||
|
||||
verify(world).setSpawnFlags(true, true);
|
||||
verify(world).setSpawnFlags(true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,7 @@ final class YamlPocketBaseRepositoryTest {
|
||||
worldId, "pocket", 20, 65, 12, PocketPortalAxis.Z
|
||||
)),
|
||||
true,
|
||||
false,
|
||||
PocketBaseBiome.CRIMSON_FOREST
|
||||
);
|
||||
YamlPocketBaseRepository repository = new YamlPocketBaseRepository(
|
||||
@@ -50,8 +51,23 @@ final class YamlPocketBaseRepositoryTest {
|
||||
|
||||
PocketBaseState loaded = repository.load().get(ownerId);
|
||||
|
||||
assertFalse(loaded.mobSpawningEnabled());
|
||||
assertFalse(loaded.hostileMobSpawningEnabled());
|
||||
assertFalse(loaded.passiveMobSpawningEnabled());
|
||||
assertTrue(loaded.returnPortal().isEmpty());
|
||||
assertEquals(PocketBaseBiome.THE_VOID, loaded.biome());
|
||||
}
|
||||
|
||||
@Test
|
||||
void migratesLegacyMobPreferenceToBothCategories() throws Exception {
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
Path stateFile = temporaryDirectory.resolve("pocket-bases.yml");
|
||||
Files.writeString(stateFile, "owners:\n " + ownerId
|
||||
+ ":\n level: 1\n mob-spawning-enabled: true\n");
|
||||
YamlPocketBaseRepository repository = new YamlPocketBaseRepository(stateFile);
|
||||
|
||||
PocketBaseState loaded = repository.load().get(ownerId);
|
||||
|
||||
assertTrue(loaded.hostileMobSpawningEnabled());
|
||||
assertTrue(loaded.passiveMobSpawningEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user