You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Biome access is gated purely on usb.biome.<key> permission nodes (BiomeCommand, BiomeConfig.getAvailableBiomes). Challenge rewards grant these via PlayerInfo.addPermissions → Vault. Two consequences:
Without a permissions plugin, biome rewards silently never apply. Grants park in the player-YAML pending-permissions list forever — and that list is re-appended on every skyworld teleport, so it also grows unboundedly (this doubling affects all permission rewards on Vault-less servers, not just biomes).
Biome unlocks are conceptually island progression (the biome is an island attribute, challenge progress is island-shared), yet they're materialized as per-player permission state. That mismatch is what forces re-grants on member join, per-member clears on restart, and the associated race conditions.
Decision
Fold into the 4.0 challenge rework (#119/#128) rather than patching 3.x:
Biome unlocks become island property. The island earns the unlock; any member with the canChangeBiome island permission picks from the island's set. Island create/restart resets unlocks naturally with island state — no per-member clearing policy needed.
Biome rewards become a first-class reward type in the challenge catalog (e.g. rewards.biomes: [jungle, ...]) instead of string-sniffing usb.biome.* out of permission rewards at runtime. reward.permission stays a pure permission grant.
The legacy importer translates existing usb.biome.* permission-reward entries into biome rewards at challenges.yml import time (Add explicit challenges.yml migration pipeline #131) — the special-casing happens once, permanently, with operator-visible logging. usb.biome.* wildcards expand against the configured biome list (warn on the rest).
The permission gate stays as an OR-fallback (hasPermission("usb.biome." + key) || island.hasUnlockedBiome(key)) for all of 4.x: donor/VIP/op access stays expressible via permissions, and legacy player-level grants keep working without a destructive migration of external permissions data.
Open sub-decisions for the 4.0 design review
Materialized vs derived storage: store the unlock set on the island, or derive it from the island's completed challenges' biome rewards (+ cache, invalidated on completion/reset)? Derived has no new persistent state, no grant/sync surface, and restart semantics follow challenge reset for free — but couples gating to the challenge subsystem and changes meaning when a challenge's rewards are edited retroactively. Leaning derived, given the catalog rework rebuilds the completion runtime anyway.
Default biomes: replace plugin.yml-default semantics with an operator-controlled defaultUnlockedBiomes config (bare biome keys, Registry-validated, warn-and-skip). Decide whether the configured default biome is force-included (guaranteed valid choice) or merely warned about when missing (operators can lock everything; matches the warn-don't-force config philosophy).
Grandfathering: whether to additionally seed island stores from the union of current members' legacy usb.biome.* nodes, or rely solely on the OR-fallback aging out.
Implementation checklist items that an earlier attempt got wrong
(from the adversarial review of branch independent-biome-unlocking, commit 65c2dc3 — player-scoped implementation, reviewed and rejected: wrong scope plus ~13 defects)
usb.biome.* wildcard rewards must not collapse to a dead literal * entry
Locale-safe key normalization (toLowerCase(Locale.ROOT)), and gate on the Registry-matched biome key, not the raw command argument (BiomeCommand already mismatches these for permissions today)
getPlayerInfo can return null (maintenance mode) — no main-thread player-file I/O or NPEs in the GUI filter path
No destructive removal of usb.biome.* nodes from external permissions plugins — leftovers are harmless under the OR gate, and removal is incomplete by construction (offline players, Player-only hook API)
Update docs/src/admin/customization.md (currently: biome access "is controlled by permissions")
Worth keeping from that branch: the dual OR gate, key normalization/dedupe approach, and the config-driven default-unlocked-set concept. The branch itself can be deleted once this issue is the design of record.
Problem
Biome access is gated purely on
usb.biome.<key>permission nodes (BiomeCommand,BiomeConfig.getAvailableBiomes). Challenge rewards grant these viaPlayerInfo.addPermissions→ Vault. Two consequences:pending-permissionslist forever — and that list is re-appended on every skyworld teleport, so it also grows unboundedly (this doubling affects all permission rewards on Vault-less servers, not just biomes).Decision
Fold into the 4.0 challenge rework (#119/#128) rather than patching 3.x:
canChangeBiomeisland permission picks from the island's set. Island create/restart resets unlocks naturally with island state — no per-member clearing policy needed.rewards.biomes: [jungle, ...]) instead of string-sniffingusb.biome.*out of permission rewards at runtime.reward.permissionstays a pure permission grant.usb.biome.*permission-reward entries into biome rewards at challenges.yml import time (Add explicit challenges.yml migration pipeline #131) — the special-casing happens once, permanently, with operator-visible logging.usb.biome.*wildcards expand against the configured biome list (warn on the rest).hasPermission("usb.biome." + key) || island.hasUnlockedBiome(key)) for all of 4.x: donor/VIP/op access stays expressible via permissions, and legacy player-level grants keep working without a destructive migration of external permissions data.Open sub-decisions for the 4.0 design review
defaultUnlockedBiomesconfig (bare biome keys, Registry-validated, warn-and-skip). Decide whether the configured default biome is force-included (guaranteed valid choice) or merely warned about when missing (operators can lock everything; matches the warn-don't-force config philosophy).usb.biome.*nodes, or rely solely on the OR-fallback aging out.Implementation checklist items that an earlier attempt got wrong
(from the adversarial review of branch
independent-biome-unlocking, commit 65c2dc3 — player-scoped implementation, reviewed and rejected: wrong scope plus ~13 defects)usb.biome.*wildcard rewards must not collapse to a dead literal*entrytoLowerCase(Locale.ROOT)), and gate on the Registry-matched biome key, not the raw command argument (BiomeCommandalready mismatches these for permissions today)getPlayerInfocan return null (maintenance mode) — no main-thread player-file I/O or NPEs in the GUI filter pathoptions.restart.clearPerms,resetChallengesOnCreate) with hard-coded reset policyusb.biome.*nodes from external permissions plugins — leftovers are harmless under the OR gate, and removal is incomplete by construction (offline players, Player-only hook API)docs/src/admin/customization.md(currently: biome access "is controlled by permissions")Worth keeping from that branch: the dual OR gate, key normalization/dedupe approach, and the config-driven default-unlocked-set concept. The branch itself can be deleted once this issue is the design of record.
Related
#119 (challenge modernization umbrella), #128 (4.0 gameplay changes), #131 (challenges.yml migration pipeline — importer home), #110 (GUI migration), PR #144 (independent plugin.yml node fixes + NPE guard shipped for 3.x).