From 3be3e4307251527d5f685aefb702bee54d726194 Mon Sep 17 00:00:00 2001 From: Dylan Garvis Date: Fri, 21 Aug 2026 22:28:34 -0400 Subject: [PATCH] fix(teleport): enforce visitor warm-up --- design/user-stories/us-008-unlock-visitor-access.md | 2 +- .../games/dmg/spigotbase/BaseTeleportManager.java | 2 +- .../java/games/dmg/spigotbase/TeleportPolicy.java | 7 +++++++ .../java/games/dmg/spigotbase/TeleportPolicyTest.java | 11 +++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/design/user-stories/us-008-unlock-visitor-access.md b/design/user-stories/us-008-unlock-visitor-access.md index e3bd5d8..138b965 100644 --- a/design/user-stories/us-008-unlock-visitor-access.md +++ b/design/user-stories/us-008-unlock-visitor-access.md @@ -21,7 +21,7 @@ As a **player with Base III**, I want to open my base to visitors so that other - [x] `/gotobase ` autocompletes bases that the requesting player is currently eligible to visit. - [x] `/visit ` aliases `/gotobase ` with identical autocomplete, including eligible bases whose owners are offline. - [x] Enabled bases remain visitable while their owners are offline. -- [x] A visitor teleport uses the destination owner's current warm-up tier. +- [x] A visitor teleport uses the destination owner's current warm-up tier, but always requires at least a one-second stationary warm-up; `/visit` and `/gotobase` never teleport instantly. - [x] Looking around is permitted, while movement between block coordinates, damage, teleportation, world change, death, logout, or a conflicting teleport cancels the visitor warm-up. - [x] Cancellation or destination failure does not consume a visitor cooldown. - [x] A safe destination is resolved at or near the owner's recorded base center. diff --git a/src/main/java/games/dmg/spigotbase/BaseTeleportManager.java b/src/main/java/games/dmg/spigotbase/BaseTeleportManager.java index e49118a..669b40b 100644 --- a/src/main/java/games/dmg/spigotbase/BaseTeleportManager.java +++ b/src/main/java/games/dmg/spigotbase/BaseTeleportManager.java @@ -95,7 +95,7 @@ final class BaseTeleportManager implements Listener { begin( visitor, owner.base().orElseThrow(), - policy.warmup(owner), + policy.visitorWarmup(owner), owner.playerId(), policy.cooldown(owner), "visit " + owner.latestName() + "'s base" diff --git a/src/main/java/games/dmg/spigotbase/TeleportPolicy.java b/src/main/java/games/dmg/spigotbase/TeleportPolicy.java index 1bfbe70..9d99e4a 100644 --- a/src/main/java/games/dmg/spigotbase/TeleportPolicy.java +++ b/src/main/java/games/dmg/spigotbase/TeleportPolicy.java @@ -25,6 +25,13 @@ public final class TeleportPolicy { }; } + public Duration visitorWarmup(PlayerState owner) { + Duration warmup = warmup(owner); + return warmup.compareTo(Duration.ofSeconds(1)) < 0 + ? Duration.ofSeconds(1) + : warmup; + } + public Duration cooldown(PlayerState player) { return switch (player.cooldownLevel()) { case 0 -> Duration.ofSeconds(settings.current().initialTeleportCooldownSeconds()); diff --git a/src/test/java/games/dmg/spigotbase/TeleportPolicyTest.java b/src/test/java/games/dmg/spigotbase/TeleportPolicyTest.java index f9e9294..b8980c2 100644 --- a/src/test/java/games/dmg/spigotbase/TeleportPolicyTest.java +++ b/src/test/java/games/dmg/spigotbase/TeleportPolicyTest.java @@ -22,6 +22,17 @@ final class TeleportPolicyTest { assertEquals(Duration.ZERO, policy.warmup(player.withTeleportLevels(3, 0))); } + @Test + void visitorWarmupHasOneSecondMinimum() { + PlayerState player = baseThreePlayer(); + + assertEquals(Duration.ofSeconds(30), policy.visitorWarmup(player)); + assertEquals( + Duration.ofSeconds(1), + policy.visitorWarmup(player.withTeleportLevels(3, 0)) + ); + } + @Test void cooldownLevelsUseApprovedDurations() { PlayerState player = baseThreePlayer();