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.
This commit is contained in:
Michael Burgess
2026-08-09 12:25:08 -04:00
parent c98f89c225
commit 576c078abf
3 changed files with 25 additions and 5 deletions
+10 -5
View File
@@ -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. - 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 **Right-click a button placed directly below the sign** to toggle it. Closed means
closed - `gate.iris-shield-material` (default bedrock), solid, blocking everything - 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 whether or not the gate is even connected right now, and travel is refused even if
interior just reflects the connection state: `gate.iris-idle-material` (default air) something manages to occupy that space anyway (flight, an elytra, a vehicle - not
if idle, or the event horizon if actively dialed. Closing needs nothing extra; just relying on block collision). It blocks in both directions: a closed shield on
if the gate has a pin set (`/sg pin <code>`), opening a closed shield prompts you the gate you're dialing *from* stops you from ever stepping in, and a closed shield
to type the code in chat within `gate.pin-timeout-seconds` before it'll open. 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 <code>`), opening a closed shield prompts you to type the code in chat
within `gate.pin-timeout-seconds` before it'll open.
## Multi-world ## Multi-world
@@ -85,6 +85,7 @@ public class GateTeleportListener implements Listener {
List<RuntimeGate> gates = gateManager.all(); List<RuntimeGate> gates = gateManager.all();
for (RuntimeGate rg : gates) { for (RuntimeGate rg : gates) {
if (!rg.isOpen() || !rg.isOutgoing() || rg.getStructure() == null || rg.getConnectedTo() == null) continue; 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()) { for (Block iris : rg.getStructure().getIris()) {
if (iris.getX() == standing.getX() && iris.getY() == standing.getY() && iris.getZ() == standing.getZ() if (iris.getX() == standing.getX() && iris.getY() == standing.getY() && iris.getZ() == standing.getZ()
&& iris.getWorld().equals(standing.getWorld())) { && iris.getWorld().equals(standing.getWorld())) {
@@ -98,6 +99,11 @@ public class GateTeleportListener implements Listener {
private void teleportPlayer(Player player, RuntimeGate entered) { private void teleportPlayer(Player player, RuntimeGate entered) {
RuntimeGate dest = entered.getConnectedTo(); RuntimeGate dest = entered.getConnectedTo();
if (dest == null) return; 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(); Location loc = player.getLocation();
Vector direction = loc.getDirection(); Vector direction = loc.getDirection();
@@ -121,6 +127,7 @@ public class GateTeleportListener implements Listener {
private void teleportEntity(Entity entity, RuntimeGate entered) { private void teleportEntity(Entity entity, RuntimeGate entered) {
RuntimeGate dest = entered.getConnectedTo(); RuntimeGate dest = entered.getConnectedTo();
if (dest == null) return; 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 if (!dest.getGate().getServerId().equals(plugin.getServerId())) return; // no cross-server carry for non-players
Location loc = entity.getLocation(); Location loc = entity.getLocation();
@@ -79,6 +79,14 @@ public class CrossServerBridge implements PluginMessageListener {
Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.getScheduler().runTask(plugin, () -> {
Player p = Bukkit.getPlayer(playerId); Player p = Bukkit.getPlayer(playerId);
if (p == null) return; 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); double yawRad = Math.toRadians(yaw);
Vector direction = new Vector(-Math.sin(yawRad), 0, Math.cos(yawRad)); Vector direction = new Vector(-Math.sin(yawRad), 0, Math.cos(yawRad));
Location landing = gateManager.computeSafeLanding(rg, direction, yaw, pitch); Location landing = gateManager.computeSafeLanding(rg, direction, yaw, pitch);