Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A OneBlock add-on for Paper. Players walk into OneBlock through the Waystation, ring their island's bell to leave, and get something back for whatever they lose to the void.

**Status:** 0.1.1. It passes all 86 of its bot checks on a throwaway Paper 26.2 server, with Waystation 0.6.0, OneBlock 1.6.4, Multiverse-Core 5.8.2, Multiverse-Inventories 5.3.6, LuckPerms and HouseRules 0.1.1. The bots walk through Waystation's real OneBlock gate. The bots are 26.1 clients connecting through ViaVersion. On Paper 26.3 it loads, sets up and reports no problems, but no bot could play there: 26.3 disconnects clients that come through ViaVersion ("Invalid move player packet"), even ones standing still. Nothing has been seen in a real game client yet. See [Tested](#tested) for the details.
**Status:** 0.1.2. Its full bot suite passed 86/86 for 0.1.1 on a throwaway Paper 26.2 server, and 0.1.2's respawn, void-death and way-bell sections pass with Waystation 0.7.0; tested with OneBlock 1.6.4, Multiverse-Core 5.8.2, Multiverse-Inventories 5.3.6, LuckPerms and HouseRules 0.1.1. The bots walk through Waystation's real OneBlock gate. The bots are 26.1 clients connecting through ViaVersion. On Paper 26.3 it loads, sets up and reports no problems, but no bot could play there: 26.3 disconnects clients that come through ViaVersion ("Invalid move player packet"), even ones standing still. Nothing has been seen in a real game client yet. See [Tested](#tested) for the details.

## What it does

Expand Down Expand Up @@ -36,7 +36,7 @@ Paper 26.1 or later, Java 25. Waystation 0.4.0 or later and OneBlock 1.6.4 (plug

With the server stopped:

1. Copy `Voidwell-0.1.1.jar` to `plugins/`.
1. Copy `Voidwell-0.1.2.jar` to `plugins/`.
2. In OneBlock, turn off two settings that fight Voidwell: `/ob autojoin false` and `/ob rebirth_on_the_island false`. With autojoin on, OneBlock cancels the trip back to a player's bell.
3. In Waystation, set the OneBlock world's gate to `hand-over: true` and turn island posts off (`island-posts.enabled: false`). The bell replaces them.
4. Put the OneBlock world in its own Multiverse-Inventories group, and the Waystation's world in a group with the lobby, so a death in OneBlock is a plain OneBlock-to-lobby swap.
Expand Down Expand Up @@ -88,15 +88,15 @@ Not tested yet:

## Troubleshooting

- **A player respawns on their island after a OneBlock death.** Voidwell sets the respawn at the Waystation's OneBlock gate, and since 0.1.1 it checks half a second later. If another plugin has moved the player back into OneBlock, Voidwell puts them at the gate and writes a console warning naming where they were moved. To find which plugin did it, start the server with `-Dvoidwell.trace=true`: every teleport and respawn is then logged with the code that caused it. Leave it off otherwise.
- **A player respawns on their island after a OneBlock death.** Voidwell sets the respawn at the Waystation's OneBlock gate and checks half a second later that they're within a block of it. If another plugin has moved them, Voidwell puts them back and writes a console warning saying where they'd been moved. Multiverse-Inventories' `apply-last-location-for-all-teleports` is the known cause: it can restore the step into the gate. Use Waystation 0.7.0 or later too. To find any other cause, start the server with `-Dvoidwell.trace=true`: every teleport and respawn is then logged with the code that caused it. Leave it off otherwise.

## Build

```bash
./gradlew build
```

The jar is `build/libs/Voidwell-0.1.1.jar`. To run the bot tests: `cd test && npm install && node fetch.mjs && VOIDWELL_WAYSTATION_JAR=<path to Waystation-0.4.0.jar> node suite.mjs 26.2`. Without `VOIDWELL_WAYSTATION_JAR` they use a small stand-in for Waystation (`test/standin`) instead.
The jar is `build/libs/Voidwell-0.1.2.jar`. To run the bot tests: `cd test && npm install && node fetch.mjs && VOIDWELL_WAYSTATION_JAR=<path to Waystation-0.4.0.jar> node suite.mjs 26.2`. Without `VOIDWELL_WAYSTATION_JAR` they use a small stand-in for Waystation (`test/standin`) instead.

## License

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group = "com.extantape"
version = "0.1.1"
version = "0.1.2"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
Expand Down
14 changes: 14 additions & 0 deletions docs/releases/v0.1.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
The real fix for the respawn on the island after a bell trip, found with 0.1.1's trace on a copy of a real server.

## Fixed

- **A OneBlock death ends at the Waystation's OneBlock gate, even with Multiverse-Inventories restoring last locations.** With `apply-last-location-for-all-teleports` on, Multiverse-Inventories put a respawned player back on their last spot in the station, which after a bell trip was the step into the OneBlock gate, and the gate then took them to their island. Half a second after every OneBlock respawn, Voidwell now checks the player is within a block of the landing (0.1.1 only checked they weren't in OneBlock), and if not, puts them back there and says so in the console.
- Use it with **Waystation 0.7.0**, where a teleport that leaves a player at a gate can't let the gate take them on their first step.

## Upgrading

Replace the jar, and Waystation's with 0.7.0. No config or data changes; no server setting needs changing.

## Tested

Bot tests on a throwaway Paper 26.2 server with Waystation 0.7.0: the rehearsal's repro (bell trip, back, void death) respawns at the gate; when the console moves the player onto the island, or onto the step into the gate, right after the respawn, Voidwell puts them back at the landing and logs it. The first-visit, void-death and way-bell sections passed 21/21 (one earlier run had a health check off by 3 points, unrelated and not seen again). The whole suite wasn't rerun for this release.
12 changes: 12 additions & 0 deletions docs/test-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

Kept by this repo's owning lead (Minecraft: Plugins Lead), per `~/Code/CLAUDE.md`, "Test self-audits". Newest first.

## 2026-11-10: the respawn, for real (0.1.2)

0.1.1's trace on the Server lead's copy found the cause: Multiverse-Inventories' `apply-last-location-for-all-teleports` restored a respawned player's last spot in the station, the step into the OneBlock gate, and the gate fired.

| Promise | Covered by |
| --- | --- |
| A respawned player put on the step into the gate ends at the landing, not on the island | bots "put on the step into the gate after the respawn, they end at the landing…", "and the gate did not take them" (the console plays Multiverse-Inventories) |
| Voidwell's check needs them within a block of the landing, not merely out of OneBlock | the same check: 0.1.1's rule let the gate step pass, because it's two blocks away |

The bot tests found one more thing: right after a teleport, a bot's position can bounce back to where it was once, which is why the check runs at half a second, before any step, and why Waystation 0.7.0's step-clear rule is the second line, not the only one.


## 2026-11-10: the respawn after a bell trip (0.1.1)

The server session's rehearsal on a copy of the real server (Paper 26.2, Waystation 0.6.0, OneBlock 1.6.4) found a void death after a bell trip and a return putting the player on their island while Voidwell said "You're back at the Waystation", in 3 of 3 runs. In this repo's suite the same steps respawn at the gate; a teleport trace showed only Voidwell's respawn there. So something on the real server moves the player afterwards.
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/com/extantape/voidwell/trip/DeathListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ public void onDropSpawn(ItemSpawnEvent e) {
item.getPersistentDataContainer().set(dropOwner, PersistentDataType.STRING, dying.toString());
}

/**
* Within a block of the landing, in its world: where a respawn should leave them. Not more: the step into the
* OneBlock gate is only about two blocks from the landing.
*/
static boolean atLanding(Location at, Location landing) {
return at.getWorld() != null && at.getWorld().equals(landing.getWorld()) && at.distanceSquared(landing) <= 1.0;
}

/** Respawn outside the OneBlock gate, even with a bed or anchor set. Runs after OneBlock's own handler. */
@EventHandler(priority = EventPriority.HIGHEST)
public void onRespawn(PlayerRespawnEvent e) {
Expand All @@ -105,11 +113,13 @@ public void onRespawn(PlayerRespawnEvent e) {
// admin can find which plugin did it (start Java with -Dvoidwell.trace=true to log every teleport).
Location promised = gate.get().clone();
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (!p.isOnline() || !plugin.bridge.inWorld(p.getLocation())) return;
if (!p.isOnline() || atLanding(p.getLocation(), promised)) return;
Location at = p.getLocation();
plugin.getLogger().warning("Something moved " + p.getName() + " back into OneBlock after their respawn (to "
+ at.getBlockX() + ", " + at.getBlockY() + ", " + at.getBlockZ() + "). Sending them to the Waystation's OneBlock gate, as Voidwell promised."
+ " To find what moved them, start the server with -Dvoidwell.trace=true.");
// Seen on a real server (2026-11-10): Multiverse-Inventories with apply-last-location-for-all-teleports
// put respawned players back on their last spot in the station, the step into the OneBlock gate.
plugin.getLogger().warning("Something moved " + p.getName() + " away from the Waystation's OneBlock gate after their respawn (to "
+ at.getWorld().getName() + " " + at.getBlockX() + ", " + at.getBlockY() + ", " + at.getBlockZ() + "). Putting them back, as Voidwell promised."
+ " Multiverse-Inventories' apply-last-location-for-all-teleports does this; -Dvoidwell.trace=true shows any other cause.");
p.teleportAsync(promised, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.PLUGIN);
}, 10L);
} else {
Expand Down
15 changes: 14 additions & 1 deletion test/suite.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,20 @@ const tests = {
await sleep(3000)
const moved = await where('Ash')
check('way bell: if another plugin moves them back after the respawn, Voidwell still puts them at the gate', atLanding(moved), JSON.stringify(moved))
check('way bell: and the console says what happened', /Something moved Ash back into OneBlock after their respawn/.test(s.since(mark)))
check('way bell: and the console says what happened', /Something moved Ash away from the Waystation's OneBlock gate after their respawn/.test(s.since(mark)))
// What Multiverse-Inventories did on the real server's copy: right after the respawn, it put the player back on their
// last spot in the station, the step into the OneBlock gate. Voidwell puts them back at the landing, and Waystation
// (0.7.0) won't fire a gate for someone a teleport put next to it until they've stepped clear.
if (REAL_WAYSTATION && GATE) {
const mark2 = s.mark()
await s.run('execute in minecraft:oneblock run tp Ash 0.5 -75 0.5', /Teleported/)
await new Promise(res => { a.once('spawn', res); setTimeout(res, 8000) })
s.cmd(`execute in minecraft:${GATE.world} run tp Ash ${GATE.x + 0.5} ${GATE.y} ${GATE.z + 0.5}`)
await sleep(4000)
const after = await where('Ash')
check('way bell: put on the step into the gate after the respawn, they end at the landing, not on the island', atLanding(after), JSON.stringify(after))
check('way bell: and the gate did not take them', !/minecraft:oneblock/.test(after.dim) && !s.since(mark2).includes('handed to Voidwell'), JSON.stringify(after))
}
a.quit(); await sleep(1000)
},
}
Expand Down
Loading