feat(pocket-base): allow return portal relocation
Release / release (push) Successful in 2m39s
CI / build (push) Successful in 1m13s

This commit is contained in:
dmg
2026-08-24 10:06:25 -04:00
parent 23e8fcec6e
commit 4518478ea1
12 changed files with 290 additions and 20 deletions
@@ -79,10 +79,6 @@ final class PocketBaseController implements Listener, Runnable {
if (pocket.level() < 1) {
return;
}
PlayerState owner = baseStates.player(player.getUniqueId(), player.getName());
if (owner.baseLevel() < 4 || owner.base().isEmpty()) {
return;
}
Block clicked = event.getClickedBlock();
World world = clicked.getWorld();
Material frameMaterial = Material.valueOf(
@@ -98,6 +94,37 @@ final class PocketBaseController implements Listener, Runnable {
if (portal.isEmpty()) {
return;
}
Optional<UUID> pocketOwner = pocketBases.ownerForPocketWorld(world.getUID());
if (pocketOwner.isPresent()) {
if (!pocketOwner.orElseThrow().equals(player.getUniqueId())) {
return;
}
if (PocketPortalGeometry.frameBlocks(portal.orElseThrow()).stream()
.anyMatch(candidate -> !pocketBases.policy().contains(
pocket.level(), candidate.x(), candidate.z()
))) {
player.sendMessage(ChatColor.RED
+ "The complete return portal must be inside your Pocket Base boundary.");
event.setCancelled(true);
return;
}
try {
pocketBases.activateReturnPortal(player.getUniqueId(), portal.orElseThrow());
} catch (IOException | RuntimeException exception) {
logger.log(Level.SEVERE, "Could not activate Pocket Base return portal", exception);
player.sendMessage(ChatColor.RED
+ "The Pocket Base return portal could not be activated.");
event.setCancelled(true);
return;
}
event.setCancelled(true);
player.sendMessage(ChatColor.GREEN + "Pocket Base return portal activated.");
return;
}
PlayerState owner = baseStates.player(player.getUniqueId(), player.getName());
if (owner.baseLevel() < 4 || owner.base().isEmpty()) {
return;
}
BaseArea area = baseBounds.area(owner);
if (PocketPortalGeometry.frameBlocks(portal.orElseThrow()).stream().anyMatch(candidate ->
!area.contains(world.getUID(), candidate.x(), candidate.y(), candidate.z()))) {
@@ -60,6 +60,17 @@ final class PocketBaseManager {
}
}
PocketBaseState activateReturnPortal(
UUID ownerId,
PocketPortalLocation portal
) throws IOException {
PocketBaseState current = states.state(ownerId);
if (current.level() < 1) {
throw new IllegalStateException("Pocket Base I is still locked");
}
return states.updateAndSave(ownerId, state -> state.withReturnPortal(portal));
}
PocketBaseState setMobSpawning(UUID ownerId, boolean enabled) throws IOException {
PocketBaseState current = states.state(ownerId);
if (current.level() < 1) {
@@ -90,11 +101,13 @@ final class PocketBaseManager {
}
PocketPortalLocation returnPortal(UUID ownerId) {
return worlds.returnPortal(ownerId);
return states.state(ownerId).returnPortal().orElseGet(
() -> worlds.returnPortal(ownerId)
);
}
boolean returnPortalIsIntact(UUID ownerId) {
return worlds.returnPortalIsIntact(ownerId);
return worlds.returnPortalIsIntact(ownerId, returnPortal(ownerId));
}
World pocketWorld(UUID ownerId) {
@@ -7,6 +7,7 @@ public record PocketBaseState(
UUID ownerId,
int level,
Optional<PocketPortalLocation> entrance,
Optional<PocketPortalLocation> returnPortal,
boolean mobSpawningEnabled
) {
public PocketBaseState {
@@ -17,8 +18,9 @@ public record PocketBaseState(
throw new IllegalArgumentException("Pocket Base level must not be negative");
}
entrance = entrance == null ? Optional.empty() : entrance;
if (level == 0 && entrance.isPresent()) {
throw new IllegalArgumentException("a locked Pocket Base cannot have an entrance");
returnPortal = returnPortal == null ? Optional.empty() : returnPortal;
if (level == 0 && (entrance.isPresent() || returnPortal.isPresent())) {
throw new IllegalArgumentException("a locked Pocket Base cannot have a portal");
}
}
@@ -27,26 +29,49 @@ public record PocketBaseState(
int level,
Optional<PocketPortalLocation> entrance
) {
this(ownerId, level, entrance, false);
this(ownerId, level, entrance, Optional.empty(), false);
}
public PocketBaseState(
UUID ownerId,
int level,
Optional<PocketPortalLocation> entrance,
boolean mobSpawningEnabled
) {
this(ownerId, level, entrance, Optional.empty(), mobSpawningEnabled);
}
public static PocketBaseState locked(UUID ownerId) {
return new PocketBaseState(ownerId, 0, Optional.empty(), false);
return new PocketBaseState(
ownerId, 0, Optional.empty(), Optional.empty(), false
);
}
public PocketBaseState withLevel(int newLevel) {
return new PocketBaseState(ownerId, newLevel, entrance, mobSpawningEnabled);
return new PocketBaseState(
ownerId, newLevel, entrance, returnPortal, mobSpawningEnabled
);
}
public PocketBaseState withEntrance(PocketPortalLocation portal) {
return new PocketBaseState(ownerId, level, Optional.of(portal), mobSpawningEnabled);
return new PocketBaseState(
ownerId, level, Optional.of(portal), returnPortal, mobSpawningEnabled
);
}
public PocketBaseState withoutEntrance() {
return new PocketBaseState(ownerId, level, Optional.empty(), mobSpawningEnabled);
return new PocketBaseState(
ownerId, level, Optional.empty(), returnPortal, mobSpawningEnabled
);
}
public PocketBaseState withReturnPortal(PocketPortalLocation portal) {
return new PocketBaseState(
ownerId, level, entrance, Optional.of(portal), mobSpawningEnabled
);
}
public PocketBaseState withMobSpawningEnabled(boolean enabled) {
return new PocketBaseState(ownerId, level, entrance, enabled);
return new PocketBaseState(ownerId, level, entrance, returnPortal, enabled);
}
}
@@ -81,9 +81,11 @@ final class PocketBaseWorldService {
);
}
boolean returnPortalIsIntact(UUID ownerId) {
PocketPortalLocation portal = returnPortal(ownerId);
boolean returnPortalIsIntact(UUID ownerId, PocketPortalLocation portal) {
World world = ensureWorld(ownerId);
if (!portal.worldId().equals(world.getUID())) {
return false;
}
Material frame = Material.valueOf(settings.current().pocketBasePortalFrameMaterial());
return PocketPortalGeometry.frameBlocks(portal).stream()
.allMatch(position -> world.getBlockAt(
@@ -43,6 +43,7 @@ public final class YamlPocketBaseRepository {
ownerId,
level,
loadPortal(yaml, path + ".entrance"),
loadPortal(yaml, path + ".return-portal"),
yaml.getBoolean(path + ".mob-spawning-enabled", false)
);
states.put(ownerId, state);
@@ -64,6 +65,9 @@ public final class YamlPocketBaseRepository {
yaml.set(path + ".level", state.level());
yaml.set(path + ".mob-spawning-enabled", state.mobSpawningEnabled());
state.entrance().ifPresent(portal -> savePortal(yaml, path + ".entrance", portal));
state.returnPortal().ifPresent(portal ->
savePortal(yaml, path + ".return-portal", portal)
);
}
Path temporary = Files.createTempFile(parent, "spigot-base-pocket-", ".yml");
try {
@@ -96,7 +100,7 @@ public final class YamlPocketBaseRepository {
if (worldId == null || worldName == null || axis == null
|| !yaml.isInt(path + ".x") || !yaml.isInt(path + ".y")
|| !yaml.isInt(path + ".z")) {
throw new IllegalArgumentException("invalid Pocket Base entrance");
throw new IllegalArgumentException("invalid Pocket Base portal");
}
return Optional.of(new PocketPortalLocation(
UUID.fromString(worldId),