ToastPacket imported net.minecraft.client.gui.components.toasts.* and
net.minecraft.client.Minecraft at class scope. When BnsNetwork.onRegister
Payloads referenced ToastPacket::handle on the dedicated server,
NoClassDefFoundError fired (the client-only classes don't exist on
the dedicated server side).
Toast feedback wasn't load-bearing — KubeJS already chat-prints the
result of every /bns verb. Players see "Sold 8x diamond for 60 spurs"
in the chat as before, and the screens re-render off the snapshot
the bridge pushes after each routed verb.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Hooks NeoForge's VillagerTradesEvent + WandererTradesEvent at data-pack
reload time and wraps every existing ItemListing so the resulting
MerchantOffer has its emerald-cost-or-result items swapped to
Numismatics coin equivalents:
1..7 emeralds -> spurs (1:1)
8..63 emeralds -> bevels (1 bevel = 8 spurs)
64+ emeralds -> cogs (1 cog = 64 spurs)
Lives in bnstoolkit instead of a third-party mod because the obvious
choice — Modrinth's 'numismatics-villager-currency' — requires NeoForge
21.1.230+ and we're on 21.1.228. The two-page Java wrapper here is
simpler than the alternatives (writing every trade override JSON via
'data-trades') AND doesn't push us into a runtime upgrade.
Includes wanderer trades. Modded villager professions are picked up
automatically since we just wrap the event's trade list as-is.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
All five screens implemented with FTB Library widgets (Panel/Widget/
SimpleTextButton). Layout matches FTB Quests/Chunks for visual
consistency with the rest of the pack.
Screens:
- TierUpgradeScreen — 13-row ladder, current tier highlighted,
Upgrade button to next tier with cost/affordability check
- ShopBrowserScreen — 9-cell item grid + detail pane, quick-sell
buttons (1/16/64) with live DR-aware price preview
- BountyBoardScreen — daily pool + active set, accept/cancel/turn-in
per state, slot-cap indicator
- PlotPurchaseScreen — plot list + detail pane, buy/release with
ownership colour-coding and tier-gated buy
- QuestLogScreen — at-a-glance tier+balance summary, active
bounties with progress bars, owned plots
HUD overlay: top-left tier name + spurs balance, registered as a
NeoForge GUI layer above the experience bar. 1Hz server push keeps
the balance current without polling client-side inventory.
Network: replaces single smoke-test packet with 7-packet contract:
- C2S: RequestEconomySnapshot, RunBnsCommand (whitelisted)
- S2C: TierStateUpdate, SellSnapshot, BountySnapshot, PlotSnapshot, Toast
Server-side bridge (ServerEconomyBridge) reads player NBT and
Numismatics-by-item-id-string for balance, routes write verbs
through the existing KubeJS /bns chat commands so KubeJS remains the
source of truth for economy logic.
Key bindings (rebindable in vanilla controls menu):
- K Civic tier ladder
- J Bounty board
- N Plot office
- H Bazaar shop
- M Quest log
FTB Library + Architectury maven repos wired in build.gradle as
compileOnly; the jar stays small since FTB Library is already in pack.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The empty-class registration was rejected by NeoForge 21.1 at mod-loading
time with:
IllegalArgumentException: class uk.sijbers.bnstoolkit.BnsToolkit
has no @SubscribeEvent methods, but register was called anyway.
That tanked the dedicated server during construct-mods. Caught only after
deploying 0.1.0 to the live server — the local /gradlew build doesn't
exercise mod-loading. Will add a gameTestServer run later to catch this
class of bug pre-deploy.
When the first @SubscribeEvent listener lands (likely PlayerLoggedInEvent
in M1), the register call comes back together with it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>