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
@@ -66,6 +66,10 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
&& arguments[1].equalsIgnoreCase("upgrade")) {
return purchasePocketUpgrade(player, state);
}
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")
&& arguments[1].equalsIgnoreCase("mobs")) {
return updatePocketMobSpawning(player, arguments[2]);
}
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("visitors")) {
return updateVisitors(player, state, arguments[1]);
}
@@ -176,6 +180,37 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
return true;
}
private boolean updatePocketMobSpawning(Player player, String mode) {
if (pocketBases == null) {
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
return true;
}
PocketBaseState current = pocketBases.state(player.getUniqueId());
if (current.level() < 1) {
player.sendMessage(ChatColor.RED + "Pocket Base I is still locked.");
return true;
}
Boolean enabled = enabledMode(mode);
if (enabled == null) {
sendUsage(player);
return true;
}
final PocketBaseState updated;
try {
updated = pocketBases.setMobSpawning(player.getUniqueId(), enabled);
} 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")
+ ChatColor.YELLOW + ".");
return true;
}
private boolean updateVisitors(Player player, PlayerState state, String mode) {
if (state.baseLevel() < 4) {
player.sendMessage(ChatColor.RED + "Base IV visitor access is still locked.");
@@ -408,14 +443,17 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
}
private void showPocketPath(Player player, PlayerState owner) {
int level = pocketBases == null ? 0 : pocketBases.state(owner.playerId()).level();
int price = level == 0
PocketBaseState pocket = pocketBases == null
? PocketBaseState.locked(owner.playerId())
: pocketBases.state(owner.playerId());
int price = pocket.level() == 0
? settings.current().pocketBaseUnlockCost()
: settings.current().pocketBaseUpgradeCost();
player.sendMessage(colorForPrerequisite(owner.baseLevel() >= 4 && owner.base().isPresent())
+ "Pocket Base " + level + ": " + ChatColor.GRAY + price + " "
+ "Pocket Base " + pocket.level() + ": " + ChatColor.GRAY + price + " "
+ settings.current().pocketBaseCurrencyMaterial().toLowerCase(Locale.ROOT)
+ " → /basesettings pocket upgrade");
+ " → /basesettings pocket upgrade; mob spawning="
+ (pocket.mobSpawningEnabled() ? "enabled" : "disabled"));
}
private void showCooldownPath(Player player, PlayerState state) {
@@ -447,13 +485,18 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
List<String> modes = arguments[0].equalsIgnoreCase("visitors")
? VISITOR_MODES
: switch (arguments[0].toLowerCase(Locale.ROOT)) {
case "pocket" -> List.of("upgrade");
case "pocket" -> List.of("upgrade", "mobs");
case "navigation", "flight", "border", "spawnable", "bossbar" -> ENABLE_MODES;
default -> List.of();
};
String prefix = arguments[1].toLowerCase(Locale.ROOT);
return modes.stream().filter(mode -> mode.startsWith(prefix)).toList();
}
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")
&& arguments[1].equalsIgnoreCase("mobs")) {
String prefix = arguments[2].toLowerCase(Locale.ROOT);
return ENABLE_MODES.stream().filter(mode -> mode.startsWith(prefix)).toList();
}
return List.of();
}
@@ -513,8 +556,8 @@ final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
private static void sendUsage(Player player) {
player.sendMessage(ChatColor.RED + "Usage: /basesettings "
+ "[status|upgrade|pocket upgrade|visitors <allowed|blocked>"
+ "|navigation <enable|disable>"
+ "[status|upgrade|pocket upgrade|pocket mobs <enable|disable>"
+ "|visitors <allowed|blocked>|navigation <enable|disable>"
+ "|flight <enable|disable>|border <enable|disable>"
+ "|spawnable <enable|disable>|bossbar <enable|disable>]");
}
@@ -60,6 +60,27 @@ final class PocketBaseManager {
}
}
PocketBaseState setMobSpawning(UUID ownerId, boolean enabled) throws IOException {
PocketBaseState current = states.state(ownerId);
if (current.level() < 1) {
throw new IllegalStateException("Pocket Base I is still locked");
}
worlds.setMobSpawning(ownerId, enabled);
try {
return states.updateAndSave(
ownerId,
state -> state.withMobSpawningEnabled(enabled)
);
} catch (IOException | RuntimeException exception) {
try {
worlds.setMobSpawning(ownerId, current.mobSpawningEnabled());
} catch (RuntimeException rollbackFailure) {
exception.addSuppressed(rollbackFailure);
}
throw exception;
}
}
Optional<UUID> ownerForPocketWorld(UUID worldId) {
return worlds.ownerForWorld(worldId);
}
@@ -6,7 +6,8 @@ import java.util.UUID;
public record PocketBaseState(
UUID ownerId,
int level,
Optional<PocketPortalLocation> entrance
Optional<PocketPortalLocation> entrance,
boolean mobSpawningEnabled
) {
public PocketBaseState {
if (ownerId == null) {
@@ -21,19 +22,31 @@ public record PocketBaseState(
}
}
public PocketBaseState(
UUID ownerId,
int level,
Optional<PocketPortalLocation> entrance
) {
this(ownerId, level, entrance, false);
}
public static PocketBaseState locked(UUID ownerId) {
return new PocketBaseState(ownerId, 0, Optional.empty());
return new PocketBaseState(ownerId, 0, Optional.empty(), false);
}
public PocketBaseState withLevel(int newLevel) {
return new PocketBaseState(ownerId, newLevel, entrance);
return new PocketBaseState(ownerId, newLevel, entrance, mobSpawningEnabled);
}
public PocketBaseState withEntrance(PocketPortalLocation portal) {
return new PocketBaseState(ownerId, level, Optional.of(portal));
return new PocketBaseState(ownerId, level, Optional.of(portal), mobSpawningEnabled);
}
public PocketBaseState withoutEntrance() {
return new PocketBaseState(ownerId, level, Optional.empty());
return new PocketBaseState(ownerId, level, Optional.empty(), mobSpawningEnabled);
}
public PocketBaseState withMobSpawningEnabled(boolean enabled) {
return new PocketBaseState(ownerId, level, entrance, enabled);
}
}
@@ -27,7 +27,7 @@ final class PocketBaseWorldService {
void loadExisting(Map<UUID, PocketBaseState> states) {
for (PocketBaseState state : states.values()) {
if (state.level() > 0) {
ensureWorld(state.ownerId());
setMobSpawning(state.ownerId(), state.mobSpawningEnabled());
}
}
}
@@ -66,6 +66,10 @@ final class PocketBaseWorldService {
return ensureWorld(ownerId);
}
void setMobSpawning(UUID ownerId, boolean enabled) {
ensureWorld(ownerId).setSpawnFlags(enabled, enabled);
}
Location arrival(UUID ownerId) {
return new Location(ensureWorld(ownerId), 0.5, PLATFORM_Y + 1, 5.5, 180.0f, 0.0f);
}
@@ -90,7 +94,8 @@ final class PocketBaseWorldService {
private World ensureWorld(UUID ownerId) {
String name = worldName(ownerId);
World world = server.getWorld(name);
if (world == null) {
boolean created = world == null;
if (created) {
WorldCreator creator = new WorldCreator(name)
.environment(World.Environment.NORMAL)
.generator(new VoidPocketChunkGenerator())
@@ -100,6 +105,9 @@ final class PocketBaseWorldService {
if (world == null) {
throw new IllegalStateException("Could not create Pocket Base world " + name);
}
if (created) {
world.setSpawnFlags(false, false);
}
ownersByWorld.put(world.getUID(), ownerId);
return world;
}
@@ -42,7 +42,8 @@ public final class YamlPocketBaseRepository {
PocketBaseState state = new PocketBaseState(
ownerId,
level,
loadPortal(yaml, path + ".entrance")
loadPortal(yaml, path + ".entrance"),
yaml.getBoolean(path + ".mob-spawning-enabled", false)
);
states.put(ownerId, state);
} catch (IllegalArgumentException ignored) {
@@ -61,6 +62,7 @@ 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());
state.entrance().ifPresent(portal -> savePortal(yaml, path + ".entrance", portal));
}
Path temporary = Files.createTempFile(parent, "spigot-base-pocket-", ".yml");