feat(heights): mount larger players without consent
Release / release (push) Successful in 4m6s
CI / build (push) Successful in 2m7s

This commit is contained in:
dmg
2026-09-12 08:35:29 -04:00
parent df44bba9c6
commit a949fd99c4
16 changed files with 585 additions and 206 deletions
+32 -9
View File
@@ -59,27 +59,43 @@ Heights corrects Purpur 2618's no-op conversion of custom potions without a vani
**Verification:** `nativeBrewingTest` executes real Purpur 2618 brewing cycles with native inventories, recipes, metadata and Paper event dispatch. It covers all four splash/lingering conversions, ingredient safety and cancellation, mixed/ordinary potions, and output compatibility with the stature adapters. World and entity boundaries are test doubles; these checks do not claim native projectile/cloud spawning, client appearance, or live logout/restart gameplay. Those remain supplementary live checks.
## Consensual carrying (US-010; live acceptance pending)
## Ride larger players (US-010)
Sneak-right-click another player with your main hand to request a ride or pickup. The larger player is always the carrier, regardless of who asks. The other player has **30 seconds** to explicitly accept:
Right-click a larger player with your **main hand** to mount immediately. Sneaking and consent are
not required. Only the smaller rider may initiate: larger players cannot force pickup of smaller
players. Either participant can end the ride, without administrator permission or rider approval:
```text
/heights carry accept
/heights carry decline
/heights carry stop
```
These commands require **no administrator permission**; settings and player-height commands still require `spigotheights.admin`. Console cannot carry players. One outstanding request per participant prevents replacement/ambiguous consent. Either participant can withdraw a pending request with `stop`; `decline` rejects an incoming request. Requests and carry tracking are not persisted.
There are no consent requests, expiry timers, or accept/decline commands. Settings and player-height
commands still require `spigotheights.admin`; console cannot carry players. Ride tracking is not
persisted. There is no remount cooldown or opt-out: a smaller player can mount again after ejection.
Both players must be available and within **4 blocks**, in the same world, when requesting and accepting. Their **effective** scale difference must be at least **three current adjustment steps** (normally `0.3`), including other plugins' attribute modifiers. Neither may already have a vehicle or passengers. Heights permits only one rider per carrier, and blocks additional/nested mounts involving an active pair.
Both players must be available and within **4 blocks**, in the same world. Their **effective** scale
difference, including attribute modifiers, must meet `carry.minimum-scale-gap` (default **0.1**,
independent of potion adjustment sizes). Zero still requires a strictly larger carrier. Neither
may already have a vehicle or passengers. Heights permits only one rider per carrier and blocks
additional/nested mounts involving an active pair. Ineligible player clicks remain uncancelled for
other plugins; only eligible carry attempts and their same-tick duplicates are consumed.
Either player can stop with **normal Minecraft dismounting**. Heights calls the rider's native `leaveVehicle()` and allows ordinary sneak-dismounts, leaving placement, falling, damage, and environmental hazards to Minecraft. There is no safe-ground requirement or search, rescue teleport, saved-position/spawn fallback, motion/fall-distance reset, invulnerability, or recovery system. Getting off above a drop is a gameplay risk.
The rider can **sneak-dismount normally**; either participant can use the stop command. Heights calls only the rider's native `leaveVehicle()` when commanded, leaving placement, falling, damage, and environmental hazards to Minecraft. There is no safe-ground requirement or search, rescue teleport, saved-position/spawn fallback, motion/fall-distance reset, invulnerability, or recovery system. Getting off above a drop is a gameplay risk.
Eligibility is rechecked every five ticks, including temporary potion application/expiry, administrator edits and external scale modifiers. Death, disconnect, plugin disable and loss of eligibility attempt native cleanup. For observed teleport events, Heights detaches the pair and lets the original teleport continue, without replaying or redirecting it. Native carrier teleports that refuse passengers before emitting an event may still require `/heights carry stop` first.
Server/plugin cancellation remains authoritative: a refused dismount leaves accurate tracking and is reported, not bypassed or called successful. A cancelled voluntary stop can be retried by either player; automatic eligibility cleanup continues checking and reports refusal once per ride rather than spamming. Lifecycle/disable refusals are logged. Native dismount events are reconciled after dispatch so a later cancellation is honored. An unrelated vehicle is never ejected during stale-pair cleanup.
**Verification:** automated domain and constructed Bukkit-event tests are not live-server evidence. Purpur 26.2 build 2618 still needs checks for scaled rider positioning and movement, ordinary dismount placement/falling, cancellation interoperability, both-role death/disconnect, portal/cross-world teleports, plugin disable and eligibility changes. No throwing or added launch force, player items, storage, or changes to throwable-potion opt-in behavior are included.
**Verification:** API/domain tests preserve native cleanup and reject rescue/motion-reset calls.
`NativeCarryRoutingTest` registers the production listener through Purpur 2618's actual Paper
plugin manager and dispatches both `PlayerInteractEntityEvent` and `PlayerInteractAtEntityEvent`.
Same-tick duplicate rider/target interactions are coalesced, including refused mounts; a fresh
interaction next tick can retry without a gameplay cooldown. Registered mount/dismount cancellation
and nonadmin stop routing are also covered. Server/player boundaries are doubled; this is not
client-packet generation or live-server evidence. Supplementary Purpur checks remain for empty/occupied
hands, scaled rider positioning and movement, native placement/falling, cancellation interoperability,
both-role death/disconnect, portal/cross-world teleports, disable and potion/admin/expiry eligibility
changes. No throwing, added launch force, player items, storage, opt-out or rescue behavior is included.
## Tiny-player launchers
@@ -94,12 +110,19 @@ height:
minimum: 0.4
maximum: 2.0
adjustment-step: 0.1
carry:
minimum-scale-gap: 0.1
launcher:
maximum-player-scale-exclusive: 0.5
speed: 1.5
cooldown-ticks: 20
```
`carry.minimum-scale-gap` is independent of potion `height.adjustment-step` and defaults to `0.1`,
including existing files without the key. It must be a finite, nonnegative number; invalid values
prevent plugin enable. Zero permits any strictly positive scale difference, **not equal-size riding**.
Edit this setting in the file and restart; min/max commands preserve it.
Player scales are stored by UUID in `plugins/SpigotHeights/state.yml`.
## Commands
@@ -136,7 +159,7 @@ Player names must match an online player exactly (case-insensitive); partial nam
The plugin JAR is written to `build/libs/`.
`check` includes `nativeBrewingTest`. It downloads the exact Purpur 26.2 build 2618 launcher (SHA-256 verified) and prepares its runtime dependencies with Paperclip's **patch-only** mode under `build/brewing-runtime/`. It does not start a server, open ports, create a world or accept the EULA. Initial/clean builds need network access; the native test JVM permits up to 1 GiB heap. No runtime implementation classes or test dependencies are packaged in the plugin. Run `./gradlew test` for the fast API/domain suite alone, or `./gradlew nativeBrewingTest` for the native brewing suite. Full verification remains `./gradlew clean check jar`.
`check` includes `nativeBrewingTest`. It downloads the exact Purpur 26.2 build 2618 launcher (SHA-256 verified) and prepares its runtime dependencies with Paperclip's **patch-only** mode under `build/brewing-runtime/`. It does not start a server, open ports, create a world or accept the EULA. Initial/clean builds need network access; the native test JVM permits up to 1 GiB heap. No runtime implementation classes or test dependencies are packaged in the plugin. Run `./gradlew test` for the fast API/domain suite alone, or `./gradlew nativeBrewingTest` for native brewing and registered carry-event routing tests. Full verification remains `./gradlew clean check jar`.
## Releases