From 576c078abf79b4e68b35013cc0c0438dea612fce Mon Sep 17 00:00:00 2001 From: Michael Burgess Date: Sun, 9 Aug 2026 12:25:08 -0400 Subject: [PATCH] Actually enforce the iris shield instead of only rendering it The shield's closed/open state only ever controlled the iris block's appearance (bedrock vs water) - the teleport listener never checked it, only whether the gate was open+outgoing. A solid bedrock block stops normal walking, but nothing stopped a player who ended up standing there anyway (flight, elytra, a vehicle) from still getting teleported, which is what was reported: iris visibly closed, travel still worked. findOutgoingGateAt() now skips any gate whose iris is closed, and both teleportPlayer()/teleportEntity() also refuse the trip if the DESTINATION's shield is closed, matching a real iris blocking whatever hits it from either side. Same check added to the cross-server delivery path in CrossServerBridge for the race where the shield closes between the proxy request and the player landing. --- README.md | 15 ++++++++++----- .../paper/listener/GateTeleportListener.java | 7 +++++++ .../stargate/paper/network/CrossServerBridge.java | 8 ++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8241257..e581dbc 100644 --- a/README.md +++ b/README.md @@ -84,11 +84,16 @@ material, unsealed interior, too far from the sign, etc. - The **iris shield** is a separate, physical thing from the wormhole connection. **Right-click a button placed directly below the sign** to toggle it. Closed means closed - `gate.iris-shield-material` (default bedrock), solid, blocking everything - - whether or not the gate is even connected right now. With the shield open, the - interior just reflects the connection state: `gate.iris-idle-material` (default air) - if idle, or the event horizon if actively dialed. Closing needs nothing extra; - if the gate has a pin set (`/sg pin `), opening a closed shield prompts you - to type the code in chat within `gate.pin-timeout-seconds` before it'll open. + whether or not the gate is even connected right now, and travel is refused even if + something manages to occupy that space anyway (flight, an elytra, a vehicle - not + just relying on block collision). It blocks in both directions: a closed shield on + the gate you're dialing *from* stops you from ever stepping in, and a closed shield + on the *destination* refuses the trip too, matching a real iris stopping anything + that hits it from either side. With the shield open, the interior just reflects the + connection state: `gate.iris-idle-material` (default air) if idle, or the event + horizon if actively dialed. Closing needs nothing extra; if the gate has a pin set + (`/sg pin `), opening a closed shield prompts you to type the code in chat + within `gate.pin-timeout-seconds` before it'll open. ## Multi-world diff --git a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/listener/GateTeleportListener.java b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/listener/GateTeleportListener.java index 5861630..09935af 100644 --- a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/listener/GateTeleportListener.java +++ b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/listener/GateTeleportListener.java @@ -85,6 +85,7 @@ public class GateTeleportListener implements Listener { List gates = gateManager.all(); for (RuntimeGate rg : gates) { if (!rg.isOpen() || !rg.isOutgoing() || rg.getStructure() == null || rg.getConnectedTo() == null) continue; + if (rg.getGate().isIrisClosed()) continue; // shield blocks travel even on a connected gate for (Block iris : rg.getStructure().getIris()) { if (iris.getX() == standing.getX() && iris.getY() == standing.getY() && iris.getZ() == standing.getZ() && iris.getWorld().equals(standing.getWorld())) { @@ -98,6 +99,11 @@ public class GateTeleportListener implements Listener { private void teleportPlayer(Player player, RuntimeGate entered) { RuntimeGate dest = entered.getConnectedTo(); if (dest == null) return; + if (dest.getGate().isIrisClosed()) { + player.sendMessage(net.kyori.adventure.text.Component.text( + "The iris on the other end is closed.", net.kyori.adventure.text.format.NamedTextColor.RED)); + return; + } Location loc = player.getLocation(); Vector direction = loc.getDirection(); @@ -121,6 +127,7 @@ public class GateTeleportListener implements Listener { private void teleportEntity(Entity entity, RuntimeGate entered) { RuntimeGate dest = entered.getConnectedTo(); if (dest == null) return; + if (dest.getGate().isIrisClosed()) return; // shield blocks arrival too if (!dest.getGate().getServerId().equals(plugin.getServerId())) return; // no cross-server carry for non-players Location loc = entity.getLocation(); diff --git a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/network/CrossServerBridge.java b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/network/CrossServerBridge.java index d1e6ce9..575f9b5 100644 --- a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/network/CrossServerBridge.java +++ b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/network/CrossServerBridge.java @@ -79,6 +79,14 @@ public class CrossServerBridge implements PluginMessageListener { Bukkit.getScheduler().runTask(plugin, () -> { Player p = Bukkit.getPlayer(playerId); if (p == null) return; + if (rg.getGate().isIrisClosed()) { + // the shield closed between the request and arrival - can't send them back + // through the proxy, so just leave them at this server's default spawn + p.sendMessage(net.kyori.adventure.text.Component.text( + "The iris on the other end closed before you arrived.", + net.kyori.adventure.text.format.NamedTextColor.RED)); + return; + } double yawRad = Math.toRadians(yaw); Vector direction = new Vector(-Math.sin(yawRad), 0, Math.cos(yawRad)); Location landing = gateManager.computeSafeLanding(rg, direction, yaw, pitch);