feat(pocket-base): allow mobs through portals
Release / release (push) Successful in 3m0s
CI / build (push) Successful in 1m24s

This commit is contained in:
dmg
2026-08-24 13:10:53 -04:00
parent 29413d36b3
commit 8b3eaad253
5 changed files with 235 additions and 9 deletions
@@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
@@ -15,6 +16,7 @@ import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -26,6 +28,7 @@ import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.BoundingBox;
final class PocketBaseController implements Listener, Runnable {
private static final int VOID_RETURN_Y = -64;
@@ -63,6 +66,7 @@ final class PocketBaseController implements Listener, Runnable {
showPortal(pocketBases.returnPortal(state.ownerId()));
}
}
scanMobPortals();
long now = System.nanoTime();
cooldownUntil.entrySet().removeIf(entry -> entry.getValue() <= now);
}
@@ -235,23 +239,61 @@ final class PocketBaseController implements Listener, Runnable {
deactivate(ownerId);
}
private void leavePocket(Player player, UUID ownerId) {
void scanMobPortals() {
for (PocketBaseState state : pocketBases.knownStates().values()) {
if (state.entrance().isPresent()) {
PocketPortalLocation entrance = state.entrance().orElseThrow();
if (isIntact(entrance)) {
transportMobs(
entrance,
mob -> teleport(mob, pocketBases.pocketArrival(state.ownerId()))
);
}
}
if (state.level() > 0 && pocketBases.returnPortalIsIntact(state.ownerId())) {
transportMobs(
pocketBases.returnPortal(state.ownerId()),
mob -> leavePocket(mob, state.ownerId())
);
}
}
}
private void transportMobs(
PocketPortalLocation portal,
Consumer<Mob> transport
) {
World world = server.getWorld(portal.worldId());
if (world == null) {
return;
}
for (Entity entity : world.getNearbyEntities(
portalBounds(portal),
candidate -> candidate instanceof Mob
)) {
if (entity instanceof Mob mob && !onCooldown(mob)) {
transport.accept(mob);
}
}
}
private void leavePocket(Entity entity, UUID ownerId) {
PocketBaseState state = pocketBases.state(ownerId);
if (state.entrance().isPresent() && isIntact(state.entrance().orElseThrow())) {
teleport(player, outsidePortal(state.entrance().orElseThrow()));
teleport(entity, outsidePortal(state.entrance().orElseThrow()));
return;
}
World fallbackWorld = server.getWorlds().get(0);
teleport(player, fallbackWorld.getSpawnLocation());
teleport(entity, fallbackWorld.getSpawnLocation());
}
private void teleport(Player player, Location destination) {
cooldownUntil.put(player.getUniqueId(), System.nanoTime() + PORTAL_COOLDOWN_NANOS);
player.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
private void teleport(Entity entity, Location destination) {
cooldownUntil.put(entity.getUniqueId(), System.nanoTime() + PORTAL_COOLDOWN_NANOS);
entity.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
}
private boolean onCooldown(Player player) {
return cooldownUntil.getOrDefault(player.getUniqueId(), 0L) > System.nanoTime();
private boolean onCooldown(Entity entity) {
return cooldownUntil.getOrDefault(entity.getUniqueId(), 0L) > System.nanoTime();
}
private void validateEntrances() {
@@ -320,6 +362,26 @@ final class PocketBaseController implements Listener, Runnable {
: new Location(world, portal.x() + 1.5, portal.y() + 1.0, portal.z() + 2.0);
}
private static BoundingBox portalBounds(PocketPortalLocation portal) {
return portal.axis() == PocketPortalAxis.X
? new BoundingBox(
portal.x() + 1.0,
portal.y() + 1.0,
portal.z(),
portal.x() + 3.0,
portal.y() + 4.0,
portal.z() + 1.0
)
: new BoundingBox(
portal.x(),
portal.y() + 1.0,
portal.z() + 1.0,
portal.x() + 1.0,
portal.y() + 4.0,
portal.z() + 3.0
);
}
private static boolean isAir(Material material) {
return material == Material.AIR || material == Material.CAVE_AIR
|| material == Material.VOID_AIR;