Bounty board + plot office were reading NBT keys that don't exist. They showed empty state regardless of reality. Now read the actual KubeJS keys (bnsBountyPool, bnsBountyActive, bnsBountyCompleted per-player; server-scope bnsPlots via KubeJS MinecraftServer mixin reflectively).
Hub UI + key rebind
B opens a new hub screen with buttons routing to each sub-screen
K/J/N/H/M direct shortcuts default to UNBOUND (rebindable for power users in vanilla controls menu)
M used to conflict with vanilla Social Interactions + FTB Chunks fullscreen map (hard conflict) — fully resolved
Audit verified B is free across vanilla 1.21.1 + all 97 pack mods
NPC dialogue + scripting integration
New /bns open <screen> command with tab-complete (hub|tier|bounty|plot|shop|questlog)
Easy NPC dialogue COMMAND actions can now open any bnstoolkit screen — verified executable path via Easy NPC's CommandExecutor.executePlayerCommand
FTB Quests reward commands, command blocks, and /trigger objectives all route through the same verb
config/easy_npc/security.cfg ships with executeAsUserCommandAllowList.ALL=bns so dialogue-triggered /bns actually runs
## Summary
Closing out all 4 review findings in one PR.
### Critical fix
Bounty board + plot office were reading NBT keys that don't exist. They showed empty state regardless of reality. Now read the actual KubeJS keys (`bnsBountyPool`, `bnsBountyActive`, `bnsBountyCompleted` per-player; server-scope `bnsPlots` via KubeJS MinecraftServer mixin reflectively).
### Hub UI + key rebind
- **B** opens a new hub screen with buttons routing to each sub-screen
- K/J/N/H/M direct shortcuts default to UNBOUND (rebindable for power users in vanilla controls menu)
- M used to conflict with vanilla Social Interactions + FTB Chunks fullscreen map (hard conflict) — fully resolved
- Audit verified B is free across vanilla 1.21.1 + all 97 pack mods
### NPC dialogue + scripting integration
- New `/bns open <screen>` command with tab-complete (`hub|tier|bounty|plot|shop|questlog`)
- Easy NPC dialogue COMMAND actions can now open any bnstoolkit screen — verified executable path via Easy NPC's `CommandExecutor.executePlayerCommand`
- FTB Quests reward commands, command blocks, and `/trigger` objectives all route through the same verb
- `config/easy_npc/security.cfg` ships with `executeAsUserCommandAllowList.ALL=bns` so dialogue-triggered `/bns` actually runs
### Network hardening
- Per-player token-bucket rate limit (10/s sustain, 20 burst)
- All STRING_UTF8 codec fields capped (`kind=8`, `args=80`, `which=16`)
- Verb whitelist replaced with structured parse → regex-validate per verb → rebuild command (no prefix-startsWith surprises)
- Tier-upgrade per-player in-flight guard (500ms) closes the concurrent-packet double-spend race
- Bridge log lines sanitised so a hostile client can't inject control chars into server logs
### Review-driven nits
- Per-player tick counter for HUD push (was a shared static — N-player skew)
- VillagerTradeRebalance: try/catch around inner; null cost-A passthrough; round-to-nearest denom split (was floor → ~10% silent price haircut); silenced spammy null-entity pre-fetch warnings from recipe-viewer mods
- SpurHud Component cache (was allocating 2 per frame)
- `ClientBnsState.completedIds` → `HashSet` (O(1) per-frame lookups)
- `BountyBoardScreen` dead branch removed
- Wire-format version bumped 0.2 → 0.4 (added OpenScreenPacket)
## Verified live
Server restart at 09:26 UTC with `bnstoolkit-0.4.1.jar`:
- `Done (3.180s)` on port 25565
- `[bnstoolkit] registered /bns open <screen> command` logged at boot
- `bnstoolkit/net] registered payloads v0.4` logged at boot
- Zero `[bns/trades] inner ItemListing ... threw` warnings (down from 100+ in 0.4.0 — the null-entity pre-fetch noise is now silenced)
- No ModLoadingException
## Test plan tomorrow
- [ ] Press B in game — HubScreen opens with tier+balance header, 5 buttons
- [ ] Each button opens its sub-screen with real data populated
- [ ] Accept a bounty via screen, kill the mob — progress reflects in BountyBoardScreen + QuestLog
- [ ] Buy a plot — PlotPurchaseScreen shows you as owner; another player sees "taken by [you]"
- [ ] Spawn an Easy NPC, configure dialog with COMMAND action `/bns open shop`, click — bazaar opens
- [ ] Try `/bns open hub` from console — should fail with "must be run as a player"
- [ ] Press the now-unbound K (default) — nothing happens (correct)
- [ ] Rebind K to OPEN_TIER in vanilla controls menu — direct shortcut works
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Post-review consolidation PR. All four open issues from the parallel
code/security/hotkey/NPC audits resolved in one ship.
Key user-facing changes:
UI
- Single keybind: B opens a hub screen with buttons to the 5 sub-screens.
- K/J/N/H/M direct shortcuts default to UNBOUND so they no longer
compete with the ~100 other mods. Rebindable in vanilla controls menu.
- M used to clash with vanilla Social Interactions + FTB Chunks
fullscreen map; fully resolved.
NPC dialogue + scripting integration
- New /bns open <screen> command. Easy NPC dialogue COMMAND actions
can now open any bnstoolkit screen on the clicking player.
- FTB Quests reward commands, command blocks, and /trigger objectives
can all route into bnstoolkit screens via the same command.
- config/easy_npc/security.cfg ships with executeAsUserCommandAllowList
.ALL=bns so dialogue-triggered /bns commands actually run.
Bug fix (this is the load-bearing item)
- Bounty board + plot office screens were reading NBT keys that don't
exist in KubeJS, so they always showed "no active bounties" and "all
plots free" no matter what was actually in the world. Fixed by reading
the real per-player keys (bnsBountyPool/Active/Completed) and the
server-scope bnsPlots compound via KubeJS' MinecraftServer mixin
(reflective, no compileOnly dep on KubeJS).
Hardening (no behavior change, defense-in-depth)
- Per-player token bucket on C2S packets (10/s sustain, 20 burst).
- All STRING_UTF8 codec fields capped at sane sizes (≤80 chars).
- Verb whitelist replaced with structured parse + per-verb regex
validation + rebuilt command. No prefix-startsWith surprises.
- Tier-upgrade in-flight guard closes the concurrent-packet
double-spend race window.
- Log lines sanitised to prevent control-char injection.
Plus the nits caught by review:
- Per-player tick counter for HUD push (was a shared static)
- VillagerTradeRebalance try/catch + null-cost-A passthrough +
round-to-nearest denom split (was floor — ~10% silent price haircut)
- SpurHud component cache (was allocating per frame)
- BountyBoardScreen dead branch removed
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Matt
merged commit f2c9cabefe into main2026-06-07 09:28:21 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closing out all 4 review findings in one PR.
Critical fix
Bounty board + plot office were reading NBT keys that don't exist. They showed empty state regardless of reality. Now read the actual KubeJS keys (
bnsBountyPool,bnsBountyActive,bnsBountyCompletedper-player; server-scopebnsPlotsvia KubeJS MinecraftServer mixin reflectively).Hub UI + key rebind
NPC dialogue + scripting integration
/bns open <screen>command with tab-complete (hub|tier|bounty|plot|shop|questlog)CommandExecutor.executePlayerCommand/triggerobjectives all route through the same verbconfig/easy_npc/security.cfgships withexecuteAsUserCommandAllowList.ALL=bnsso dialogue-triggered/bnsactually runsNetwork hardening
kind=8,args=80,which=16)Review-driven nits
ClientBnsState.completedIds→HashSet(O(1) per-frame lookups)BountyBoardScreendead branch removedVerified live
Server restart at 09:26 UTC with
bnstoolkit-0.4.1.jar:Done (3.180s)on port 25565[bnstoolkit] registered /bns open <screen> commandlogged at bootbnstoolkit/net] registered payloads v0.4logged at boot[bns/trades] inner ItemListing ... threwwarnings (down from 100+ in 0.4.0 — the null-entity pre-fetch noise is now silenced)Test plan tomorrow
/bns open shop, click — bazaar opens/bns open hubfrom console — should fail with "must be run as a player"🤖 Generated with Claude Code