Compare commits

...

7 Commits

Author SHA1 Message Date
Matt ad9a261627 pack: v0.36.0 — bnstoolkit 0.4.1 (NBT fix + hub UI + NPC integration + hardening)
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>
2026-06-07 09:27:47 +00:00
Matt bae44d0178 Merge pull request 'docs: rewrite bnstoolkit handoff for feature-complete state' (#73) from docs/bnstoolkit-feature-complete into main 2026-06-07 02:43:56 +00:00
Matt 016985a5fc docs: rewrite bnstoolkit handoff for feature-complete state
The original handoff doc described an empty M0 scaffold with placeholder
screens to "discuss tomorrow". 0.3.1 actually ships the full UI plus
villager trade rebalance, so the doc now is a testing guide for the
working features rather than a discussion-points list.

Includes the architecture diagram for how Java + KubeJS interact, the
key bindings table, a step-by-step testing path, and a known-gaps list
for future iteration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 02:43:42 +00:00
Matt a7f95691bb Merge pull request 'pack: v0.35.0 — bnstoolkit feature-complete UI + villager rebalance' (#72) from feature/v0.35.0-bnstoolkit-ui-full into main 2026-06-07 02:42:43 +00:00
Matt 23efdee804 pack: actually include bnstoolkit-0.3.1.jar in v0.35.0
The previous commit deleted 0.1.1 from the gitignored mods dir but
git swallowed the add for 0.3.1 because pack/overrides/mods is
ignored. Re-adding with -f.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 02:42:06 +00:00
Matt 1859613f1f pack: v0.35.0 — bnstoolkit 0.3.1 (feature-complete UI + villager rebalance)
bnstoolkit jumps from M0 (empty scaffold) straight to feature complete:

UI screens (all FTB Library widgets, opens via keybinds):
  K  Civic tier ladder + upgrade
  J  Bounty board (accept/cancel/turn-in)
  N  Plot office (buy/release)
  H  Bazaar sell shop (1/16/64 quick-sell)
  M  Quest log (status overview)

HUD: top-left tier + spurs balance overlay, refreshed 1Hz server-side.

Villager trade rebalance (NEW): the mod now hooks VillagerTradesEvent
+ WandererTradesEvent and swaps every emerald cost/result for the
Numismatics equivalent (1..7 emeralds -> spurs, 8..63 -> bevels, 64+
-> cogs). This was the blocked Modrinth dep — the third-party mod
options either required a NeoForge bump (numismatics-villager-currency
1.1.0 wants 21.1.230+, we're on 21.1.228) or hand-written JSON for
every trade (data-trades). The in-tree wrapper is cleaner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 02:41:49 +00:00
Matt a41d8cb531 Merge pull request 'docs: bnstoolkit postmortem' (#71) from docs/bnstoolkit-discussion-postmortem into main 2026-06-07 00:19:21 +00:00
5 changed files with 138 additions and 154 deletions
+86 -152
View File
@@ -1,180 +1,114 @@
# bnstoolkit M0 — discussion points for testing session
# bnstoolkit feature-complete — testing notes for 2026-06-07 evening
> Built 2026-06-07 (early hours UTC). Shipped via pack v0.34.0 then hotfixed via v0.34.1.
> Shipped: pack v0.35.0 / bnstoolkit 0.3.1.
> Repo: http://10.16.5.102:3000/minecraft/bnstoolkit
> Jar: `bnstoolkit-0.1.1.jar` (15.6 KB)
> Server status: ✅ live + verified at 02:41 UTC.
## ⚠ Server status: needs a manual Start
## What's in the build
The first deploy (`0.1.0`) crashed the dedicated server during mod
construction — see "Bug caught + fixed" below. The fixed jar (`0.1.1`)
is already staged in `/srv/fast/brass-sigil-server/server/mods/`, but
the JVM exited cleanly so systemd didn't auto-restart and I deliberately
didn't try to brute-force the admin panel credentials.
### 5 screens (FTB Library widgets — matches FTB Quests look)
**To bring the server back: click Start in the web admin panel
([http://glados:8080](http://glados:8080)) when you wake up.** The new
boot should load `bnstoolkit-0.1.1` cleanly. Verification:
All opened via key bindings. Rebindable in vanilla controls menu under
"Brass and Sigil" category.
```
sudo journalctl -u brass-sigil-server.service --since "2 minutes ago" \
| grep -E "bnstoolkit|Done \([0-9]+|ModLoadingException"
```
Look for `[bnstoolkit] common setup complete` AND no `ModLoadingException`.
## Bug caught + fixed (0.1.0 → 0.1.1)
```
IllegalArgumentException: class uk.sijbers.bnstoolkit.BnsToolkit
has no @SubscribeEvent methods, but register was called anyway.
```
I called `NeoForge.EVENT_BUS.register(this)` in the @Mod constructor
without yet having any `@SubscribeEvent` methods on the class. NeoForge
21.1 treats that as fatal at construct-mods time. Removed the call;
will come back together with the first real event handler in M1.
**Why local `./gradlew build` didn't catch it:** the build step only
compiles + jars. The construct-mods phase only runs when a real
NeoForge instance loads the mod. Action item for M1: add a
`gameTestServer` Gradle run config that boots the dedicated server
profile in CI-style so this class of bug fails locally next time.
## What this is
The skeleton of the custom companion mod. It loads cleanly on the client
and the dedicated server, registers nothing visible yet, and exposes the
package layout we'll fill in over M1M4. The point of this milestone is:
1. Tooling proven end-to-end (Java 21, ModDevGradle, NeoForge 21.1.228, jar in pack)
2. Mod-id, package, and resource paths locked so future commits only ADD code
3. One C2S/S2C packet pair wired to prove the network plumbing
4. Placeholder screens in place so wiring keybinds to real UI is M1's first task
## Open decisions — please walk through these tomorrow
Each is marked with `TODO(matt …)` in the source.
### 1. Key bindings (BnsKeyBindings.java)
Defaults I picked, all unbinding is in the vanilla controls menu:
| Binding | Key | Opens |
| Key | Screen | What it does |
|---|---|---|
| Tier ladder | T | `TierUpgradeScreen` |
| Bounty board | B | `BountyBoardScreen` |
| Plot map | P | `PlotPurchaseScreen` |
| **K** | Civic Tier ladder | 13 rows, current tier highlighted. Right pane shows next tier's cost + perks + Upgrade button. Disabled if you can't afford it or are at Sovereign. |
| **J** | Bounty Board | Today's 5-bounty pool + any active set. Detail pane: target, count, reward, progress. Accept / Cancel / Turn In buttons by state. |
| **N** | Plot Office | Plot list with ownership state-coding (yours / taken / free). Detail pane: size, chunks, price. Buy / Release buttons. |
| **H** | Bazaar shop | 9-cell sell item grid. Click an item, see DR-aware effective price for current tier. Sell 1 / 16 / 64 quick buttons. |
| **M** | Quest Log | At-a-glance status: tier + balance + active bounties (with progress bars) + owned plots. |
**Question**: any conflicts with mods you already use? `T` is vanilla's
chat-open IIRC — I should probably move it. Pick a free combo when you
playtest, I'll bump the defaults.
### HUD overlay
### 2. Screen open triggers — keybind only, or block + keybind?
Top-left of the screen, always visible: `<tier-colour><tier-name>` on
one line, `<balance> spurs` on the second. Refreshes 1Hz from server
push — picks up coin pickups immediately, no inventory polling.
Each major screen has two viable open paths:
Auto-hides when F1 is pressed or chat is open (standard NeoForge HUD
layer behavior).
- **Keybind** (already drafted): bnstoolkit binds T/B/P
- **In-world block**: e.g. a "Bounty Board" block placed at the spawn
guild building. Right-click opens the screen. More immersive, ties the
feature to a physical location.
### Villager trade rebalance
Both can coexist easily — they're not exclusive. I'd default to "both for
TierUpgrade + Plots, keybind-only for QuestLog, block-only for ShopBrowser
and BountyBoard". But that's a 30-second call you make tomorrow.
Every emerald cost or result in vanilla + modded villager trades is
now Numismatics:
- 1..7 emeralds → spurs
- 8..63 → bevels (1 bevel = 8 spurs)
- 64+ → cogs (1 cog = 64 spurs)
### 3. FTB Library UI swap timing
Works automatically on:
- All 14 vanilla villager professions
- Wandering trader (generic + rare)
- Any modded villager professions (we wrap the event's list, no enum
of professions needed)
Right now the placeholder screens extend vanilla `Screen`. The
architecture doc commits to `dev.ftb.mods.ftblibrary.ui.BaseScreen` for
visual consistency with FTB Quests / Chunks. The swap is one line per
screen — happens in M1 alongside the first real screen layout.
This was the "blocked on API" item. Built it into bnstoolkit instead
of pulling a third-party mod, because the cleanest Modrinth option
needed NeoForge 21.1.230+ and we're on 228.
Reason it's deferred: the FTB maven coords need to be wired into
`build.gradle`, and I don't want to pin the wrong artifact name. Will
verify against the pack's installed FTB Library version (2101.1.31) before
the M1 build.
### 4. HUD overlay layout
The architecture doc shows a small panel "tier-icon | tier-name | spurs"
at top-left. Drop me a sketch when you have time and I'll lay it out for
M4. Current placeholder draws nothing — won't appear in game at all yet.
### 5. Network smoke-test packet
`RequestTierStatePacket` / `TierStateUpdatePacket` are the only wire
pair I shipped. They prove the codec + handler pattern. M1 fills in the
server-side handler that reads `player.persistentData.bnsTier` (the
KubeJS-owned NBT) and replies. **No client code yet sends the request**
the round trip is dormant in M0.
### 6. Mod settings screen
Not addressed. Will be a small screen in M4 with toggles for the HUD,
keybind reminders, and (eventually) a colour-blind mode for tier ranks.
Low priority unless you've got a specific UX concern.
---
## Files added/changed (PR for v0.34.0)
## Testing path
```
pack/overrides/mods/bnstoolkit-0.1.0.jar NEW (15.6 KB)
pack/pack.lock.json version 0.33.0 -> 0.34.0
docs/bnstoolkit-discussion-points-2026-06-07.md NEW (this file)
/bns me # confirm Peasant tier 1
/give @s numismatics:spur 500 # give spurs for upgrades
K # open tier ladder
# click Upgrade — should pay 200 and bump you to Farmer
/bns admin tier set <you> 5 # jump to Knight for plot testing
K # confirm new tier visible
H # open bazaar
# click diamond, click Sell 1 (you'll need a diamond in inventory)
J # open bounty board, accept one
# kill the target mobs — progress shows in chat AND the screen
# turn in via the J screen
N # open plot office, buy p001
M # quest log shows everything
# Test villager trades:
/summon villager ~ ~ ~
# Right-click the villager once it picks a profession (place a workstation)
# Trades should show spurs/bevels/cogs instead of emeralds
```
The bnstoolkit source itself lives at `http://10.16.5.102:3000/minecraft/bnstoolkit`
— one initial commit on `main` with the full M0 scaffold. Build:
## Architecture
```
cd /home/matt/dev/bnstoolkit
./gradlew build
KubeJS server_scripts/economy/ ← single source of truth for state + logic
│ (chat commands via server.getCommands)
ServerEconomyBridge.java ← reads NBT, sums Numismatics coins,
routes write verbs through /bns
↑↓
4 snapshot + 2 command packets
↑↓
ClientBnsState.java ← single static state holder
Screens read this each frame
```
## What you'll see in-game when you log in
Source of truth stays in KubeJS. Java is just the UI layer and a thin
write-path proxy. If you want to rebalance prices or add bounties, you
do it in KubeJS — Java auto-reflects.
**Nothing yet.** That's the point of M0 — it loads cleanly with zero
visible surface. Verification path:
## Known gaps for future iteration
1. Server log: grep for `[bnstoolkit] mod entry constructed` and
`common setup complete (M0 scaffold — no surface yet)`
2. Client log on connect: `registered 3 key bindings under key.category.bnstoolkit`
3. Controls menu → "Brass and Sigil" category → 3 unbound-looking lines
(Open Tier Ladder, Open Bounty Board, Open Plot Map) on T/B/P
| Gap | Where to fix |
|---|---|
| No client toast notifications — feedback comes via KubeJS chat messages | Add a dist-isolated `ClientToastDispatcher` class (0.4.0) |
| Bazaar grid doesn't tooltip the per-unit price | One `addMouseOverText` per `ItemCell` |
| Plot office doesn't show a minimap | Render bluemap PNG snippet as background or use FTB Chunks' map widget |
| Quest log doesn't show FTB Quests progression | Hook into FTB Quests' API once first chapter ships |
| Numismatics coins as bunch — no fractional change | Bazaar pays in highest denom that fits; player converts at the Bank |
| Wanderer rare-trade rebalance is greedy (every rare item costs are converted) | Profession-aware exclusion list if anything weird sneaks through |
If you bind one of those keys and press it in-game, **nothing happens**.
That's correct — the handlers are M1 work.
## File map
## Milestones — concrete next deliverables
| File | Purpose |
|---|---|
| `pack/overrides/mods/bnstoolkit-0.3.1.jar` | The mod itself (95 KB) |
| `pack/overrides/kubejs/server_scripts/economy/*.js` | State + business logic (unchanged from v0.33.0) |
| `pack/overrides/world/serverconfig/ftbranks/ranks.snbt` | 13-tier rank config (unchanged) |
| `docs/bnstoolkit-architecture.md` | Original architecture spec |
| Milestone | Scope | Estimated work |
|---|---|---|
| M1 | TierUpgradeScreen + KubeJS bridge (tier/balance) + FTB Library swap | 1 evening |
| M2 | QuestLogScreen + ShopBrowserScreen | 1 evening |
| M3 | BountyBoardScreen + PlotPurchaseScreen | 1 evening |
| M4 | SpurHud, mod settings screen, polish | 1 evening |
Each milestone ends with a deployable jar + a screen you can poke in
game. None of them require server downtime — drop the new jar in
`pack/overrides/mods/`, the wrapper sync handles the rest.
## Quick recovery
If the mod misbehaves and bricks the world:
```
# 1. Stop the server
sudo systemctl stop brass-sigil-server.service
# 2. Pull the jar out of the live mods dir
sudo rm /srv/fast/brass-sigil-server/server/mods/bnstoolkit-0.1.0.jar
# 3. Restart
sudo systemctl start brass-sigil-server.service
```
That leaves `pack/overrides/mods/bnstoolkit-0.1.0.jar` in the pack repo
so the next sync would put it back — to fully revert, also `git revert`
the v0.34.0 commit on the pack.
Source repo: http://10.16.5.102:3000/minecraft/bnstoolkit (main = 14dc69f at time of pack ship).
@@ -0,0 +1,50 @@
#Easy NPC Security Configuration
#
#Permission values use enum names: ALL, MODERATORS, GAMEMASTERS, ADMINS, OWNERS.
#Legacy numeric Minecraft command permission levels are accepted and rewritten to enum names.
#
#normalPlayerCommandLevel: Max command level for normal player-owned imports (default: ALL)
#creativePlayerCommandLevel: Max command level for creative non-admin imports (default: MODERATORS)
#maxAdminImportedCommandLevel: Max command level for admin imports (default: ADMINS)
#serverTrustedCommandLevel: Max command level for server-side trusted imports without player context (default: ADMINS)
#blockUnsafeNpcCommands: Blocks critical server management commands from NPC command execution (default: true)
#npcSpawnRateLimitCreative: Max new NPCs a creative (non-admin) player may spawn via browser per minute (default: 5)
#npcSpawnRateLimitAdmin: Max new NPCs an admin player may spawn via browser per minute (default: 20)
#Feature values use enum names: NORMAL_PLAYER, CREATIVE_PLAYER, ADMIN, SERVER_TRUSTED.
#feature.SPAWN_NPC: Minimum role required to spawn new NPCs from the preset browser (default: CREATIVE_PLAYER)
#feature.WORLD_PRESET: Minimum role required to import/export world presets (default: CREATIVE_PLAYER)
#feature.CUSTOM_PRESET: Minimum role required to import/export custom server presets (default: CREATIVE_PLAYER)
#feature.LOCAL_PRESET: Minimum role required to export presets locally to the client (default: NORMAL_PLAYER)
#feature.DEFAULT_PRESET_IMPORT: Minimum role required to import built-in default presets (default: CREATIVE_PLAYER)
#feature.URL_RESOURCE: Minimum role required to use URL-based skin loading (default: CREATIVE_PLAYER)
#Sat Jun 06 23:34:43 UTC 2026
blockUnsafeNpcCommands=true
creativePlayerCommandLevel=MODERATORS
executeAsUserCommandAllowList.ADMINS=
executeAsUserCommandAllowList.ALL=bns
executeAsUserCommandAllowList.GAMEMASTERS=
executeAsUserCommandAllowList.MODERATORS=
executeAsUserCommandAllowList.OWNERS=
feature.BASE_ATTRIBUTE=CREATIVE_PLAYER
feature.COMBAT_ATTRIBUTE=CREATIVE_PLAYER
feature.COMMAND_ACTION=CREATIVE_PLAYER
feature.CUSTOM_PRESET=CREATIVE_PLAYER
feature.DEFAULT_PRESET_IMPORT=CREATIVE_PLAYER
feature.DIALOG=NORMAL_PLAYER
feature.INTERACT_BLOCK_ACTION=CREATIVE_PLAYER
feature.LOCAL_PRESET=NORMAL_PLAYER
feature.MOVEMENT=CREATIVE_PLAYER
feature.OBJECTIVE=CREATIVE_PLAYER
feature.OPEN_TRADING_ACTION=CREATIVE_PLAYER
feature.POSITION=CREATIVE_PLAYER
feature.SCOREBOARD_ACTION=CREATIVE_PLAYER
feature.SPAWN_NPC=CREATIVE_PLAYER
feature.TRADING=CREATIVE_PLAYER
feature.URL_RESOURCE=CREATIVE_PLAYER
feature.WORLD_PRESET=CREATIVE_PLAYER
maxAdminImportedCommandLevel=ADMINS
normalPlayerCommandLevel=ALL
npcSpawnRateLimitAdmin=20
npcSpawnRateLimitCreative=5
serverTrustedCommandLevel=ADMINS
unsafeNpcCommands=ban,ban-ip,banlist,debug,deop,difficulty,forceload,function,gamerule,kick,op,pardon,reload,save-all,save-off,save-on,schedule,setidletimeout,setworldspawn,spawnpoint,spreadplayers,stop,whitelist
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -1,13 +1,13 @@
{
"$schema": "Brass-and-Sigil pack.lock.json - generated, do not edit by hand unless you know what you are doing",
"name": "Brass and Sigil",
"version": "0.34.1",
"version": "0.36.0",
"minecraft": "1.21.1",
"loader": {
"type": "neoforge",
"version": "21.1.228"
},
"lockedAt": "2026-06-07T00:18:08Z",
"lockedAt": "2026-06-07T09:27:18Z",
"mods": [
{
"source": "modrinth",