Paradaux
IssuesPAR-108Done
0

Bedrock: @Arg OfflinePlayer resolver does a Mojang name lookup → ghost UUIDs

Found in the cross-plugin Bedrock/Floodgate audit. Highest-leverage remaining fix — it's the binding resolver for every @Arg(...) OfflinePlayer command across Treasury and Business.

Bug: commander/resolvers/OfflinePlayerResolver.resolve tries Bukkit.getOfflinePlayerIfCached(token) then falls back to Bukkit.getOfflinePlayer(token) (deprecated). On an online-mode server (DC) that String overload does a blocking Mojang lookup, which FAILS for Bedrock players (their .-prefixed names aren't in Mojang's DB) — returning a fabricated/offline UUID, never the real Floodgate UUID. A cached Bedrock player (joined before, in usercache under .name) resolves fine; a not-yet-cached one resolves to a ghost.

Consumer impact:

  • Treasury /eco, /fine, /transactions audit, /tax triggermostly safe: each guards with hasPlayedBefore() right after, so a synthetic is rejected (just a worse error path + a needless Mojang call on the async thread).
  • Business RequestCommands (HIGH): offer/hire and the transfer begin/confirm/cancel/complete/reject family take @Arg("user") OfflinePlayer with NO guard — hiring/transferring to a never-seen player by name writes the fabricated UUID into firm_staff/proprietor records, so the real (Bedrock) player is never actually employed/granted. Ghost rows.

Recommended fix (one change, fixes all consumers): drop the Mojang fallback. Either (a) return the cache-only result and Optional.empty() when uncached — the framework then rejects with "unknown target" and no command acts on a ghost (cleanest; minor message regression vs the current synthetic-so-the-handler-can-message design), or (b) build the synthetic from a locally-computed offline UUID (UUID.nameUUIDFromBytes("OfflinePlayer:"+name)) instead of the Mojang call, preserving the "handler decides" contract but removing the network hit — then add hasPlayedBefore() guards to Business RequestCommands.offer/beginTransfer to stop ghost rows. Realty uses its own cache-only resolver and is unaffected.

Note: framework change → needs publishToMavenLocal + treasury/business rebuilds to take effect.

Comments

tesks · Jun 6, 2026, 1:03 PM

Fixed on hibernia-framework develop (889594c). Resolver is now cache-only — uncached names return empty so the framework rejects them; no Mojang call, no fabricated UUID. Verified treasury + business rebuild green against the new framework (published to mavenLocal).

Deploy chain: this is a framework change, so for release/CI builds the new hibernia jar must be published to repo.paradaux.io (./gradlew :treasury-api… — i.e. hibernia publish), and treasury + business rebuilt to bundle it. Until then their shaded jars carry the old resolver. (Local mavenLocal is updated for dev builds.)

Behavioural note: a never-seen name now fails with the framework's generic "invalid target" instead of a plugin-specific message — acceptable, and the data-integrity win (no ghost employment/proprietor rows from /firm hire/transfer) is the point. Cached players, including Bedrock under their .-prefixed name, resolve as before.

tesks · Jun 6, 2026, 11:49 PM

Released and rolled out to consumers.

  • hibernia-framework PR #6 merged → 1.0.2 published (main tip def658c). Version bumped from the already-released v1.0.1 so no collision.
  • Bumped io.paradaux:hibernia-framework1.0.2 in the command plugins that carry @Arg OfflinePlayer commands and rebuilt clean against it:
    • treasury (a674fb2)
    • business-rian (0b6cf8b)
    • treasury-api-plugin (4e17430)

Not bumped: treasury-ingest (1.0.1) — no command surface, doesn't use the resolver; and business-api (compileOnly 0.1.2) — the API jar doesn't bundle the resolver and the SPI types it references are unchanged. Both fine to leave; can align later if desired.

Verified locally against 1.0.2 from mavenLocal (treasury + business unit suites green; treasury-api-plugin compiles). Their CI/release builds will resolve 1.0.2 from repo.paradaux.io.

Activity

  • ParadauxIO linked a commit — Commit 0b6cf8b — Bump hibernia-framework to 1.0.2 for the Bedrock OfflinePlayer resolver fix (PAR-108)Jun 7, 2026, 2:49 PM
  • tesks commentedJun 6, 2026, 11:49 PM
  • ParadauxIO linked a commit — Commit 0b6cf8b — Bump hibernia-framework to 1.0.2 for the Bedrock OfflinePlayer resolver fix (PAR-108)Jun 6, 2026, 11:49 PM
  • ParadauxIO linked a commit — Commit 889594c — Bedrock: resolve @Arg OfflinePlayer from cache only, no Mojang (PAR-108)Jun 6, 2026, 11:45 PM
  • ParadauxIO changed status to Status → DoneJun 6, 2026, 11:45 PM
  • ParadauxIO linked a pull request — PR #6 merged — Release: develop → main (v1.0.2)Jun 6, 2026, 11:45 PM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → main (v1.0.2)Jun 6, 2026, 9:51 PM
  • tesks commentedJun 6, 2026, 1:03 PM
  • tesks changed status to Status → Pending ReleaseJun 6, 2026, 1:03 PM
  • ParadauxIO linked a commit — Commit 889594c — Bedrock: resolve @Arg OfflinePlayer from cache only, no Mojang (PAR-108)Jun 6, 2026, 12:58 PM
  • tesks created the issueJun 6, 2026, 12:44 PM