Compare commits

...

16 Commits

Author SHA1 Message Date
Matt 7949c2a3bb Merge pull request 'pack: v0.41.3 — Journal becomes the 'what's available + where to go' view' (#85) from feature/journal-town-board into main 2026-06-08 18:30:29 +00:00
Matt 9a3b3985ee pack: v0.41.3 — Journal becomes the "what's available" view
bnstoolkit 0.8.2 -> 0.8.3.

Adventurer's Journal rebuilt as the read-only town-board view:
- Available Bounties (today's pool of 5) with rewards
- Plots For Sale (every plot with state)
- Exchange Rates (each wealth-token with effective rate)
- Civic Tier (current + next cost)
- Your Status (active bounty progress, owned plots, wealth)

Each section heads with "Visit X Clerk at Y" so players know which
spawn building to walk to. The Journal is still read-only; all
transactions happen at the NPCs there.

Content scrolls via PanelScrollBar on the right.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 18:30:28 +00:00
Matt 1b0fa0168a Merge pull request 'pack: v0.41.2 — Exchange rename + click-refresh fix' (#84) from hotfix/exchange-rename into main 2026-06-08 18:22:48 +00:00
Matt 93c68325fe pack: v0.41.2 — rename Counting House to Exchange + fix click-refresh bug
bnstoolkit 0.8.1 -> 0.8.2.

Counting House -> Exchange (more intuitive naming).
- /bns open counthouse -> /bns open exchange
- screen title shown as "Exchange"
- Lang + docs updated

Click-doesn't-refresh-detail bug: ItemCell / BountyRow / PlotRow now
call getGui().refreshWidgets() after updating their static `selected`
field, so the detail sub-panel re-renders with the new selection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 18:22:47 +00:00
Matt 95a564c5e3 Merge pull request 'pack: v0.41.1 — UI pass + spawn-building setup doc' (#83) from hotfix/ui-pass into main 2026-06-08 18:14:51 +00:00
Matt f0e6187ed0 pack: v0.41.1 — UI pass + spawn-building-setup doc
bnstoolkit 0.8.0 -> 0.8.1.

UI pass on every transactional screen:
- Counting House grid now 4-cell (one per wealth-token), with
  inventory count badge on each cell
- Detail pane shows on-hand count + friendly display name
- Sell buttons grey out when you don't have enough
- Bottom-row Refresh replaced with smart Back/Close — fixes the
  button overlap with sub-panel internals
- Panel heights reduced (height-30 -> height-50) so internal
  buttons don't clash with the screen-level action row

Naming cleanup:
- Dropped "shop" alias (Counting House is the only correct name)
- Added "journal" as the user-facing screen key for Adventurer's
  Journal

New docs/spawn-building-setup.md covers:
- /bns admin spawn-init flow for FTB Teams init
- Per-building Easy NPC dialogue setup (Counting House Clerk,
  Bounty Officer, Plot Clerk, Civic Registrar)
- Pre-wipe testing checklist for every feature

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 18:14:50 +00:00
Matt 263b8c59d8 Merge pull request 'pack: v0.41.0 — plot config JSON + FTB Chunks integration' (#82) from feature/v0.41.0-plot-config-ftb-integration into main 2026-06-08 17:53:35 +00:00
Matt a39bfee803 pack: v0.41.0 — plot system JSON config + FTB Chunks integration
bnstoolkit 0.7.0 -> 0.8.0.

Plot system migrated from static Java + KubeJS to a runtime
configurable, FTB-Chunks-integrated system.

* Plot definitions now live at world/serverconfig/bnstoolkit/plots.json.
  Default file ships with 3 starter plots; edit + /bns admin plot reload.
* /bns admin plot define <id> <name> <price>: add a plot in-game by
  standing in the chunk. Re-run with same id to add multi-chunk plots.
* /bns admin plot list / remove / reload + /bns admin spawn-init.
* /bns admin spawn-init creates Spawn (cyan #00BFFF) + Plots (gold
  #FFD700) server teams via FTB Teams + claims all defined plots for
  the Plots team. Idempotent.
* /bns plot buy now: pays via combined cash+bank, unclaims chunks
  from Plots team, claims for buyer's personal FTB Teams team.
* /bns plot release: reverse — chunks go back to Plots team, 50% refund.
* Plot chunk guard: ClaimedChunkEvent.BEFORE_UNCLAIM subscriber blocks
  manual unclaim of plot chunks (player gets chat hint redirecting
  to /bns plot release).
* KubeJS plots.js retired (.retired suffix in repo as documentation).

On the map: cyan zones = Spawn team protection (manually claimed),
gold zones = Plots team (for sale), other team colors = owned plots.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 17:53:34 +00:00
Matt 03f650ce3b Merge pull request 'pack: v0.40.0 — drop Alex's Mobs + bounty board fix + Numismatics polish' (#81) from feature/v0.40.0-drop-am-polish into main 2026-06-08 17:20:10 +00:00
Matt 35cbd84146 pack: v0.40.0 — drop Alex's Mobs + bounty board fix + Numismatics polish
Drops Alex's Mobs from the pack (105 -> 103 mods total). The 4 AM-mob
bounty entries are replaced with vanilla 1.21.x mob bounties (Breeze,
Bogged, Evoker, Vindicator, Piglin Brute, Ravager).

Bounty board bug fix: KubeJS PlayerEvents.loggedIn now ensures the
daily pool is rolled on first login. Previously the pool only rolled
when a player ran /bns bounty list, so the bounty board screen showed
empty for anyone who'd never typed that command.

/bns tier upgrade now uses combined cash+bank deduction via
NumismaticsBridge.spend (Java). Players who've sold a fortune into
their bank can now spend it on tier upgrades directly.

Every player gets a bound white_card on first login — no more "I need
to craft + bind a card before I can spend at a Vendor block" friction.

bnstoolkit 0.6.2 -> 0.7.0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 17:20:09 +00:00
Matt c9f57dae9e Merge pull request 'pack: v0.39.2 — Adventurer's Journal + smart Back button' (#80) from hotfix/adventurers-journal into main 2026-06-08 16:20:03 +00:00
Matt 83949d63b5 pack: v0.39.2 — Adventurer's Journal + smart Back button
bnstoolkit 0.6.1 -> 0.6.2.

Journal (formerly Quest Log) reworked to be useful: active bounties
with progress bars, current Counting House rates per wealth-token,
owned plots. FTB Quests stays as a separate system (footer line
points players to their Quest Book item).

Back button: opens-from-Hub uses FTB Library's previous-screen
mechanism so Close becomes Back and returns to Hub. NPC-opened
journals (no previous screen) get a regular Close.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 16:20:03 +00:00
Matt 68aae5c917 Merge pull request 'pack: v0.39.1 — bnstoolkit 0.6.1 sub-panel sizing hotfix' (#79) from hotfix/sub-panel-sizing into main 2026-06-08 15:51:32 +00:00
Matt 0818fe8eab pack: v0.39.1 — bnstoolkit 0.6.1 sub-panel sizing hotfix
Hub screen showed blank content area: title bar + 3 buttons rendered,
but the status panel in between was empty. Same latent bug affected
every screen with a sub-panel (Tier, Counting House, Bounty, Plot,
Quest Log).

Root cause: FTB Library's Panel.refreshWidgets order is
addWidgets → recurse-into-child-panels.refreshWidgets → alignWidgets.
So when the parent's alignWidgets sets a sub-panel's size, the
sub-panel's children have already aligned themselves using the
sub-panel's pre-size dimensions (0x0). Every Row got setSize(0, 12)
→ invisible.

Fix: size sub-panels BEFORE add() in the parent's addWidgets so the
recursive refreshWidgets sees correct dimensions. Parent's
alignWidgets now only positions the top-level action buttons.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 15:50:51 +00:00
Matt f269cc4861 Merge pull request 'pack: v0.39.0 — Counting House + remove Create: Encased' (#78) from feature/v0.39.0-counting-house into main 2026-06-08 15:34:48 +00:00
Matt ae183f27de pack: v0.39.0 — Counting House refactor + remove Create: Encased
Major economy / spawn-design pivot.

## Removed: Create: Encased
The mod's `chorium_ingot` → `creative_casing` chain unlocked 60+
infinite-output creative-tier blocks via fully renewable inputs (rose
quartz + chorus fruit + lava). Single creative_mechanical_drill in a
player base would have produced enough diamonds to trivialize the
civic ladder. Removing the mod was simpler than KubeJS recipe overrides
since none of the mod's other content (wood-variant encased shafts,
coloured casings) was load-bearing for our pack.

Parallel audit of 14 other Create-family mods confirmed none have
similar exploits — only Create: Encased was the breaker.

## Counting House refactor
What used to be "Bazaar" became "Counting House" — a wealth exchange,
not a general shop. Conceptual model:
- Players accumulate diamonds / netherite / ancient debris through
  adventure and mining (the wealth-store axis)
- Counting House converts these to spurs, credited directly to the
  player's Numismatics bank account
- Iron / gold / coal / lapis / redstone are no longer here — those
  belong on player-run Numismatics Vendor blocks at market prices

SellCatalogue cut from 9 items to 4 (diamond, diamond_block,
netherite_ingot, ancient_debris). Diminishing-returns floor tightened
50% -> 30%.

## Numismatics integration
NumismaticsBridge (reflection — no hard dep) wires up:
- ensureAccount on PlayerLoggedInEvent (every player gets a personal
  account on first login)
- balanceOf / deposit / deduct for bank operations
- SpurBalance.totalSpurs = inventory coins + own bank balance, no card
  lookups required
- HUD overlay now shows three lines: tier / cash on hand / bank
  balance — total wealth visible regardless of cards

## /bns commands migrated to Java
`/bns sell` and `/bns wallet` are now Java-handled
(CountingHouseCommands.java). Bank credit happens atomically with
inventory pull. KubeJS `02_sell_shop.js` retired (.retired suffix
preserved in the repo as documentation).

## Hub status-only
Removed the Civic Tier / Bazaar / Bounty / Plot routing buttons.
Transactional screens are now only reachable via NPC dialogue
(`/bns open <screen>` from an Easy NPC COMMAND action). The hub
shows tier + cash + bank + total + slot caps; footer reads "Visit
spawn NPCs for trades, bounties, plots, tier upgrades."

This locks the spawn-as-civic-centre design: players have to travel.

## bnstoolkit jar
0.5.1 -> 0.6.0 (network version 0.4 -> 0.5; TierStateUpdatePacket
now carries cashOnHand + bankBalance separately).

## Pack manifest
102 -> 104 mods after Encased removal. Manifest republished on glados
(publish-pack.py) before restart so the wrapper's sync sees the right
target state. Without that step, the sync was reverting our changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 15:34:25 +00:00
8 changed files with 173 additions and 39 deletions
+143
View File
@@ -0,0 +1,143 @@
# Spawn building setup — wiring NPCs to bnstoolkit screens
How to give players access to the Exchange / Bounty Board / Plot
Office / Civic Registrar via Easy NPC dialogue at spawn.
## Prerequisites (one-time, post-wipe)
```
/setworldspawn ~ ~ ~ # set the canonical world spawn
/bns admin spawn-init # creates Spawn (cyan) + Plots (gold)
# FTB Teams and claims existing plots
```
If you also want spawn protection: switch to the Spawn team and claim
your spawn town chunks via the FTB Chunks map (M key, left-click).
## The five screens you can expose
| Screen | Open command | What it does |
|---|---|---|
| Hub | `/bns open hub` | Status overview (tier, balance, bounties, plots) |
| Exchange | `/bns open exchange` | Wealth exchange — sell diamonds/netherite/ancient debris for spurs |
| Bounty Board | `/bns open bounty` | Today's 5 bounties + accept/cancel/turn-in |
| Plot Office | `/bns open plot` | Buy / release plots |
| Civic Tier | `/bns open tier` | Pay spurs to upgrade tier |
| Adventurer's Journal | `/bns open journal` | Status overview + active bounties + exchange rates |
`hub` and `journal` are status-only (read). The other four are
transactional — they should be gated behind an NPC at the relevant
spawn building.
## Per-building setup (Exchange example)
Repeat for each of the 4 transactional buildings, varying just the
screen name and NPC name.
### 1. Spawn the NPC
In creative mode, get an Easy NPC spawn egg:
```
/give @s easy_npc:custom_egg
```
Right-click the ground where you want the NPC. It spawns a customisable
NPC.
### 2. Configure appearance + name
Right-click the NPC → **Configuration** (or use the wrench).
- **Skin**: pick a fitting villager / human skin
- **Name**: "Exchange Clerk" (or whatever fits the building)
- **Profession**: optional — purely cosmetic
### 3. Configure dialog
In the NPC config → **Dialog** tab.
Create a new STANDARD dialog. Suggested text:
> Welcome to the Exchange. We exchange diamonds, netherite, and
> ancient debris for spurs deposited directly to your bank account.
> Care to make a trade?
Add a **Button** to this dialog:
- **Label**: "Open the exchange"
- **Type**: ACTION
- **Action**: COMMAND
- **Command**: `bns open exchange`
- **Execute as user**: ✅ (must be enabled)
- **Permission level**: 0 (no special permission needed)
- **Trailing action**: CLOSE_DIALOG (optional — closes the dialog after click)
Add another button "Goodbye" → CLOSE_DIALOG for the polite exit.
### 4. Configure trigger
In the NPC config → **Actions** tab:
- Event: **ON_INTERACTION**
- Action: **OPEN_DEFAULT_DIALOG**
Now when a player right-clicks the NPC, the dialog opens. Clicking
"Open the exchange" runs `/bns open exchange` as that player and
the Exchange screen pops up.
## Building-to-screen mapping
| Building | NPC name suggestion | Open command |
|---|---|---|
| Exchange | Exchange Clerk | `bns open exchange` |
| Adventurer's Guild | Bounty Officer | `bns open bounty` |
| Plot Office | Plot Clerk | `bns open plot` |
| Civic Hall / Town Hall | Civic Registrar | `bns open tier` |
| (anywhere — optional) | Quartermaster | `bns open hub` |
| (anywhere — optional) | Cartographer | `bns open journal` |
## Security gate (one-time, already shipped)
The Easy NPC config at `/srv/fast/brass-sigil-server/server/config/
easy_npc/security.cfg` has `executeAsUserCommandAllowList.ALL=bns`,
so dialogue actions can run `/bns ...` on the player's behalf. This
ships with the pack as of v0.36.0 — no manual edit required after
fresh installs.
## Testing your setup
1. Build the building
2. Place the NPC
3. Configure the dialog as above
4. Switch to survival, walk up, right-click the NPC
5. Click the action button
6. The screen should open on your client immediately
If nothing happens:
- Check chat for errors ("Blocked execute-as-user command root ...")
- If you see that, the security.cfg allowlist is missing `bns` — see
the security gate section above
- Otherwise: check the server log for `[bnstoolkit] /bns open <screen>`
to confirm the command actually fired
## What if I just want to test as op (no NPC)
You can always run `/bns open <screen>` directly from chat or console
to open a screen for yourself. The op-level requirement is none — the
command works for any player. NPCs are just the in-world surface.
## Pre-wipe testing checklist (current world)
Each feature, where you'd manually test it before the wipe:
| Feature | How to test |
|---|---|
| Hub | Press B → see status panel with tier/cash/bank/total/bounties/plots |
| Exchange | `/bns open exchange` → 4 items in grid (diamond, diamond_block, netherite_ingot, ancient_debris), inventory count badge bottom-right of each cell, fee + per-unit price in detail pane, Sell 1/16/64 buttons grey out when you don't have enough |
| Sell flow | `/give @s minecraft:diamond 64` → click diamond → Sell 16 → bank balance ticks up, sell button updates |
| Bounty Board | `/bns open bounty` → see today's pool, click a bounty for detail, accept, kill the targets, turn in |
| Plot Office | `/bns open plot` → see 3 default plots, buy p001 (tier 3+ required), check FTB Chunks map M — chunk should turn your team colour |
| Plot unclaim guard | After buying a plot, try to unclaim its chunk via FTB Chunks UI → should reject with redirect message |
| Civic Tier | `/give @s numismatics:spur 500` or `/bns wallet deposit ...``/bns open tier` → click Upgrade — pays from cash + bank combined |
| Wallet | `/bns wallet`, `/bns wallet deposit 50`, `/bns wallet withdraw 50` |
| Auto-bound card | Look in inventory — should already have a white_card bound to your account |
| Villager rebalance | Trade with a villager — emerald-side replaced with spurs/bevels/cogs |
| Quest Log auto-open population | Press B → click Journal → should show active bounties, rates, plots |
If any of these break, ping with the details. Pre-wipe is the time to find these bugs.
@@ -59,12 +59,14 @@ const BOUNTY_POOL = [
{ id: 'b_wither_skel_3', name: 'Slay 3 Wither Skeletons', target: 'minecraft:wither_skeleton', count: 3, type: 'kill', reward: 150, source: "Adventurer's Guild" }, { id: 'b_wither_skel_3', name: 'Slay 3 Wither Skeletons', target: 'minecraft:wither_skeleton', count: 3, type: 'kill', reward: 150, source: "Adventurer's Guild" },
{ id: 'b_shulker_3', name: 'Slay 3 Shulkers', target: 'minecraft:shulker', count: 3, type: 'kill', reward: 200, source: "Adventurer's Guild" }, { id: 'b_shulker_3', name: 'Slay 3 Shulkers', target: 'minecraft:shulker', count: 3, type: 'kill', reward: 200, source: "Adventurer's Guild" },
// ── Boss tier (rare picks) ── // ── Boss tier (rare picks) — all vanilla 1.21.x ──
{ id: 'b_warden_1', name: 'Slay the Warden', target: 'minecraft:warden', count: 1, type: 'kill', reward: 800, source: "Adventurer's Guild" }, { id: 'b_warden_1', name: 'Slay the Warden', target: 'minecraft:warden', count: 1, type: 'kill', reward: 800, source: "Adventurer's Guild" },
{ id: 'b_voidworm_1', name: 'Destroy a Void Worm', target: 'alexsmobs:void_worm', count: 1, type: 'kill', reward: 1200, source: "Adventurer's Guild" }, { id: 'b_breeze_3', name: 'Hunt 3 Breezes', target: 'minecraft:breeze', count: 3, type: 'kill', reward: 250, source: "Adventurer's Guild" },
{ id: 'b_warped_mosco_1', name: 'Slay a Warped Mosco', target: 'alexsmobs:warped_mosco', count: 1, type: 'kill', reward: 700, source: "Adventurer's Guild" }, { id: 'b_bogged_5', name: 'Slay 5 Bogged', target: 'minecraft:bogged', count: 5, type: 'kill', reward: 120, source: "Adventurer's Guild" },
{ id: 'b_farseer_1', name: 'Slay the Farseer', target: 'alexsmobs:farseer', count: 1, type: 'kill', reward: 900, source: "Adventurer's Guild" }, { id: 'b_evoker_2', name: 'Slay 2 Evokers', target: 'minecraft:evoker', count: 2, type: 'kill', reward: 350, source: "Adventurer's Guild" },
{ id: 'b_skreecher_1', name: 'Slay a Skreecher', target: 'alexsmobs:skreecher', count: 1, type: 'kill', reward: 400, source: "Adventurer's Guild" }, { id: 'b_vindicator_4', name: 'Slay 4 Vindicators', target: 'minecraft:vindicator', count: 4, type: 'kill', reward: 180, source: "Adventurer's Guild" },
{ id: 'b_piglin_brute_2', name: 'Slay 2 Piglin Brutes', target: 'minecraft:piglin_brute', count: 2, type: 'kill', reward: 400, source: "Adventurer's Guild" },
{ id: 'b_ravager_1', name: 'Hunt down a Ravager', target: 'minecraft:ravager', count: 1, type: 'kill', reward: 500, source: "Adventurer's Guild" },
]; ];
// Index pool by id for O(1) lookup // Index pool by id for O(1) lookup
@@ -344,4 +346,14 @@ ServerEvents.commandRegistry(event => {
event.register(bountyCmd); event.register(bountyCmd);
}); });
// ─── First-login pool roll ─────────────────────────────────────────
// Without this, new players see an empty Bounty Board until they type
// /bns bounty list (which triggers ensurePoolFresh). The board reads
// bnsBountyPool from NBT; bnsBountyPool stays empty until rollDailyPool
// runs. This hook closes the gap so the screen is populated the first
// time it's opened.
PlayerEvents.loggedIn(event => {
ensurePoolFresh(event.player);
});
console.info('[bns/bounty_engine] loaded — ' + BOUNTY_POOL.length + ' bounties in catalogue'); console.info('[bns/bounty_engine] loaded — ' + BOUNTY_POOL.length + ' bounties in catalogue');
@@ -109,19 +109,20 @@ ServerEvents.commandRegistry(event => {
} }
const nextTier = cur + 1; const nextTier = cur + 1;
const nextCfg = bnsTier.config(nextTier); const nextCfg = bnsTier.config(nextTier);
const balance = countCoins(player);
if (balance < nextCfg.cost) { // Combined cash+bank deduction via bnstoolkit Java bridge.
// Pulls inventory coins first, then bank for the remainder.
const Bridge = Java.loadClass('uk.sijbers.bnstoolkit.economy.NumismaticsBridge');
const ok = Bridge.spend(player, nextCfg.cost);
if (!ok) {
const cash = countCoins(player);
const bank = Bridge.balanceOf(player);
player.tell(Text.red( player.tell(Text.red(
'Upgrade to ' + nextCfg.name + ' costs ' + nextCfg.cost + ' spurs. You have ' + balance + '.' 'Upgrade to ' + nextCfg.name + ' costs ' + nextCfg.cost + ' spurs. ' +
'You have ' + cash + ' cash + ' + bank + ' bank = ' + (cash + bank) + ' total.'
)); ));
return 0; return 0;
} }
const took = takeCoins(player, nextCfg.cost);
if (took < nextCfg.cost) {
giveCoins(player, took);
player.tell(Text.red('Coin removal failed — refunded. Try again.'));
return 0;
}
// Promote via FTB Ranks // Promote via FTB Ranks
const rankId = TIER_RANK_ID[nextTier]; const rankId = TIER_RANK_ID[nextTier];
Utils.server.runCommandSilent('ftbranks add ' + player.username + ' ' + rankId); Utils.server.runCommandSilent('ftbranks add ' + player.username + ' ' + rankId);
Binary file not shown.
Binary file not shown.
+2 -24
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", "$schema": "Brass-and-Sigil pack.lock.json - generated, do not edit by hand unless you know what you are doing",
"name": "Brass and Sigil", "name": "Brass and Sigil",
"version": "0.38.1", "version": "0.41.3",
"minecraft": "1.21.1", "minecraft": "1.21.1",
"loader": { "loader": {
"type": "neoforge", "type": "neoforge",
"version": "21.1.228" "version": "21.1.228"
}, },
"lockedAt": "2026-06-08T14:42:41Z", "lockedAt": "2026-06-08T18:30:00Z",
"mods": [ "mods": [
{ {
"source": "modrinth", "source": "modrinth",
@@ -130,17 +130,6 @@
"size": 38629, "size": 38629,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "create-encased",
"versionId": "3sUW27Ho",
"version": "1.8.1-ht1",
"path": "mods/Create Encased-1.21.1-1.8.1-ht1.jar",
"url": "https://cdn.modrinth.com/data/hSSqdyU1/versions/3sUW27Ho/Create%20Encased-1.21.1-1.8.1-ht1.jar",
"sha1": "5ca3d7674676dda5dd472b0433505a54d5e920d0",
"size": 2574837,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "bellsandwhistles", "slug": "bellsandwhistles",
@@ -1065,17 +1054,6 @@
"size": 193381, "size": 193381,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "alexs-mobs(1.21.1)",
"versionId": "KSgki4uc",
"version": "1.22.17",
"path": "mods/alexsmobs-1.22.17.jar",
"url": "https://cdn.modrinth.com/data/EmNhnNnt/versions/KSgki4uc/alexsmobs-1.22.17.jar",
"sha1": "5070e8070f60650d773bfd9f1d031496f8f50259",
"size": 26620546,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "item-obliterator", "slug": "item-obliterator",