Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c6698268 | ||
|
|
bc2f9d8e25 |
@@ -130,9 +130,22 @@ description: Chronological record of material decisions affecting the Spigot Bas
|
||||
- Preserved unit-test coverage through server-independent dialog specifications and adapted Bukkit test fixtures to Purpur's registry-aware API behavior.
|
||||
- Verified 128 tests and the Java 25 plugin artifact with `./gradlew clean check jar`.
|
||||
|
||||
## 2026-08-25 — Static dialog command actions
|
||||
|
||||
- Replaced macro command templates with static run-command actions for fixed dialog buttons.
|
||||
- Added regression coverage for the generated run-command action and its unchanged command payload.
|
||||
- Verified `/basesettings` opens without command exceptions on a local Purpur 26.2 build 2618 server.
|
||||
- Verified the plugin artifact with `./gradlew clean check jar`.
|
||||
|
||||
## 2026-08-24 — Pocket Base mob portal travel
|
||||
|
||||
- Added five-tick collision scanning that transports non-player living mobs through active Pocket Base entrance and return portals.
|
||||
- Reused safe player destinations and server-spawn fallback behavior while excluding items, projectiles, vehicles, and other non-living entities.
|
||||
- Applied the existing two-second portal cooldown to mobs to prevent immediate repeated transport.
|
||||
- Verified the feature and Java 25 plugin artifact with `./gradlew clean check jar`.
|
||||
|
||||
## 2026-08-28 — Safe Pocket Base fall returns
|
||||
|
||||
- Cleared accumulated fall distance whenever portal or void-return travel teleports an entity, preventing carried-over fall damage at the destination.
|
||||
- Added regression coverage for players falling out of a Pocket Base and returning to the server spawn.
|
||||
- Verified the fix and plugin artifact with `./gradlew clean check jar`.
|
||||
|
||||
@@ -33,6 +33,7 @@ As a **Pocket Base owner**, I want to connect my normal base to my Pocket Base w
|
||||
- [x] Breaking the active return frame disables return travel through it.
|
||||
- [x] Return travel falls back to the server spawn when the owner has no valid normal-world entrance.
|
||||
- [x] Falling below Y -64 performs the same safe return without void damage.
|
||||
- [x] Portal and void-return travel clears accumulated fall distance so players do not take carried-over fall damage after landing at the destination.
|
||||
- [x] Players who disconnect inside a Pocket Base remain there when they reconnect.
|
||||
- [x] Moving the normal base deactivates its entrance while preserving the Pocket Base world and its contents.
|
||||
- [x] Portal ownership and the active entrance location persist through restarts.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
type: User Story
|
||||
title: "US-020: Control a base through a dialog UI"
|
||||
description: Let players inspect and control their base through Minecraft's native dialog interface.
|
||||
status: in-progress
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-020: Control a base through a dialog UI
|
||||
@@ -21,7 +21,9 @@ As a **player**, I want a dialog-based base dashboard so that I can inspect prog
|
||||
- [x] `ui` is offered through command autocomplete.
|
||||
- [x] Non-player execution remains safely rejected.
|
||||
- [x] Automated tests verify dialog routing and generated controls.
|
||||
- [ ] The dialog uses Purpur's supported Paper dialog API and opens on the production Purpur 26.2 runtime without missing-class or command exceptions.
|
||||
- [x] Fixed dialog commands use Purpur static run-command actions rather than macro templates.
|
||||
- [x] A rendering regression test verifies fixed command buttons do not throw `No variables in macro`.
|
||||
- [x] The dialog uses Purpur's supported Paper dialog API and opens on the production Purpur 26.2 runtime without missing-class or command exceptions.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
final class BaseSettingsDialogFactory {
|
||||
private static final int DIALOG_WIDTH = 420;
|
||||
@@ -366,13 +367,17 @@ final class BaseSettingsDialogFactory {
|
||||
private static ActionButton render(ButtonSpec button) {
|
||||
ActionButton.Builder builder = ActionButton.builder(Component.text(button.label()))
|
||||
.width(button.width())
|
||||
.action(DialogAction.commandTemplate(button.command()));
|
||||
.action(DialogAction.staticAction(commandClick(button.command())));
|
||||
if (!button.tooltip().isEmpty()) {
|
||||
builder.tooltip(Component.text(button.tooltip()));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static ClickEvent<ClickEvent.Payload.Text> commandClick(String command) {
|
||||
return ClickEvent.runCommand(command);
|
||||
}
|
||||
|
||||
sealed interface DialogSpec permits ListSpec, MultiSpec, ConfirmationSpec, NoticeSpec {
|
||||
DialogContent content();
|
||||
}
|
||||
|
||||
@@ -289,6 +289,7 @@ final class PocketBaseController implements Listener, Runnable {
|
||||
|
||||
private void teleport(Entity entity, Location destination) {
|
||||
cooldownUntil.put(entity.getUniqueId(), System.nanoTime() + PORTAL_COOLDOWN_NANOS);
|
||||
entity.setFallDistance(0.0F);
|
||||
entity.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,24 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseSettingsDialogFactoryTest {
|
||||
@Test
|
||||
void createsStaticRunCommandActionWithoutMacroVariables() {
|
||||
ClickEvent<ClickEvent.Payload.Text> action = BaseSettingsDialogFactory.commandClick(
|
||||
"/basesettings status"
|
||||
);
|
||||
|
||||
assertEquals(ClickEvent.Action.RUN_COMMAND, action.action());
|
||||
ClickEvent.Payload.Text payload = assertInstanceOf(
|
||||
ClickEvent.Payload.Text.class,
|
||||
action.payload()
|
||||
);
|
||||
assertEquals("/basesettings status", payload.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsControlsForEveryBaseSettingAndConfirmedPurchase() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
@@ -310,6 +311,37 @@ final class PocketBaseControllerTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void voidReturnClearsAccumulatedPlayerFallDistance() {
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
UUID pocketWorldId = UUID.randomUUID();
|
||||
World pocketWorld = mock(World.class);
|
||||
World fallbackWorld = mock(World.class);
|
||||
Location destination = new Location(pocketWorld, 0.5, -65.0, 0.5);
|
||||
Location spawn = new Location(fallbackWorld, 10.5, 70.0, 10.5);
|
||||
Player player = mock(Player.class);
|
||||
PlayerMoveEvent event = mock(PlayerMoveEvent.class);
|
||||
PocketBaseManager pocketBases = mock(PocketBaseManager.class);
|
||||
Server server = mock(Server.class);
|
||||
|
||||
when(pocketWorld.getUID()).thenReturn(pocketWorldId);
|
||||
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(event.getPlayer()).thenReturn(player);
|
||||
when(event.getTo()).thenReturn(destination);
|
||||
when(server.getWorlds()).thenReturn(List.of(fallbackWorld));
|
||||
when(fallbackWorld.getSpawnLocation()).thenReturn(spawn);
|
||||
when(pocketBases.ownerForPocketWorld(pocketWorldId)).thenReturn(Optional.of(ownerId));
|
||||
when(pocketBases.state(ownerId)).thenReturn(
|
||||
new PocketBaseState(ownerId, 1, Optional.empty())
|
||||
);
|
||||
PocketBaseController controller = controller(server, pocketBases);
|
||||
|
||||
controller.onMove(event);
|
||||
|
||||
verify(player).setFallDistance(0.0F);
|
||||
verify(player).teleport(spawn, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mobReturnFallsBackToServerSpawnWithoutValidEntrance() {
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
|
||||
Reference in New Issue
Block a user