Compare commits

...

13 Commits

Author SHA1 Message Date
Matt c723a42628 cbc: disable all cannon self-failure
Cannons are expensive to build; users were losing them to RNG
self-destruction (squib, barrel charge burst, overload, interrupted
ignition, catastrophic failure). Per-cannon failure is configurable
via a single master switch in createbigcannons-server.toml:

  [failure]
  disableAllCannonFailure = true

This zeroes out all six failure paths in one go. Applied to the live
server and tracked here under defaultconfigs/ so future server resets
preserve it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 19:39:44 +00:00
Matt 83abbc90d3 Merge pull request 'pack: v0.28.0 - Steam n Rails + Copycats+/Aero weight' (#46) from feature/v0.28.0-rails-and-aero-bridge into main 2026-05-28 19:30:04 +00:00
Matt 5a24801f48 pack: v0.28.0 — Steam 'n' Rails (port) + Copycats+/Aero weight bridge
Adds two mods on top of v0.27.0:

  - Create: Steam 'n' Rails 0.2.0+neoforge-mc1.21.1 (stable). The
    official Steam 'n' Rails team skipped 1.21.1, so this is the
    "Porters of Railways" community port — actively maintained,
    310k downloads on the beta line and 0.2.0 just hit stable.
    Brings train QoL: track tools, semaphores, conductor cap,
    smokestacks, narrow-gauge tracks, etc.

  - Copycats+ aeronautics weight 1.1.1. Small interop mod so
    copycat blocks contribute weight to Create: Aeronautics
    contraptions instead of being treated as massless. ~15KB.

All deps satisfied by mods already in the pack (Create, Copycats+,
Sable, Create: Aeronautics).

Tested live: server boots, port 25565 binds, KubeJS loads 0/0 errors,
aerocopycats logs HELLO FROM COMMON SETUP, Railways registers
55+53+238 entries across its three creative tabs. No new crashes.

Known noise: Steam 'n' Rails ships optional loot tables for tracks
of wood types from many other mods (TwilightForest, BYG, TFC, Blue
Skies, Hex Casting, BoP, etc.). Mods absent from this pack produce
~276 'Unknown registry key' ERROR lines once at boot. Harmless —
loot tables for items that don't exist just fail to parse and get
skipped. Standard behavior for Rails on lean packs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 19:29:43 +00:00
Matt 5e816e1a7a Merge pull request 'kubejs: use kubejs-create builder API for flax press recipe' (#45) from refactor/supplementaries-bridges-kubejs-create-api into main 2026-05-28 19:08:42 +00:00
Matt d2cc179ebd kubejs: use kubejs-create builder API for flax press recipe
Now that v0.27.0 ships kubejs_create, the high-level
event.recipes.create.pressing(out, in) builder works, replacing the
raw event.custom({type: 'create:pressing', ...}) JSON shim we needed
before.

Verified live on the server -- recipe loads cleanly:
[bns/supplementaries_bridges] added flax -> string press recipe

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:55:43 +00:00
Matt 847ca460ff Merge pull request 'pack: v0.27.0 - Create QoL + aesthetic batch (KubeJS Create + Encased + Bells & Whistles)' (#44) from feature/v0.27.0-create-batch into main 2026-05-28 18:51:06 +00:00
Matt 96d4608e4c pack: v0.27.0 — Create QoL + aesthetic batch
Three small Create addons, no overlap with current 18-mod Create stack:

  + kubejs-create v2101.3.1-build.18
      Recipe builders for KubeJS scripts. Will replace the raw
      Create:pressing JSON in supplementaries_bridges.js with a
      cleaner event.recipes.create.* call.

  + create-encased v1.8.1-ht1
      Encased/cased variants of Create kinetic blocks (shafts,
      gearboxes, cogwheels in compact casings) for cleaner builds.

  + bellsandwhistles v0.4.7-1.21.1
      Train whistles, bells, signal lights, station decoration.

All three already deployed live; pack-version.json bumped, MC
server restarted to sync. No mod-load errors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:55:55 +00:00
Matt 61321e5fcd Merge pull request 'ops: cap ZFS ARC at 2GB on glados' (#43) from ops/zfs-arc-cap into main 2026-05-28 13:52:59 +00:00
Matt a8ff660114 Merge pull request 'backup-scheduler: dont replay missed daily slots on startup' (#42) from fix/backup-scheduler-no-replay into main 2026-05-28 13:52:58 +00:00
Matt ab63908d04 Merge pull request 'v0.24.0 -> v0.26.0: content + storage batch' (#41) from feature/v0.26.0-content-and-storage-batch into main 2026-05-28 13:52:56 +00:00
Matt fd211aae51 ops: cap ZFS ARC at 2GB on glados
The cold ZFS pool is a backup tier only — hot data (MC world, Gitea,
Directus DB, containers) lives on NVMe LVM, not ZFS. So ARC's default of
50% RAM was wasted: nothing it could cache was hot enough to matter, and
the ~16GB cache crowded MC out of RAM, pushing 2.4GB into swap.

This file lives in the repo for reproducibility; it gets dropped into
/etc/modprobe.d/ on the host and applied immediately via
/sys/module/zfs/parameters/zfs_arc_{max,min}.

Effect on glados:
  before:  used=27/31GB, free=748MB, ARC=15.6GB
  after:   used=~10GB, free=~14GB, ARC=1.0GB (1-2GB range)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:05:52 +00:00
Matt f81bb29fda backup-scheduler: don't replay missed daily slots on startup
The wake loop's daily-times branch was firing one missed slot per minute
on service restart. With a schedule like "00:00,04:00,08:00,12:00,16:00,
20:00" and a restart at 19:00, the wrapper would catch up by firing 5
backups at 1-minute intervals -- each a full-world zip of the same
in-memory world. Pure waste: the live world hasn't diverged across the
missed clock slots, the snapshots would be near-identical, and the rapid
save-off/save-on churn impacts player experience.

Source comment already documented the intended behaviour ("Doesn't catch
up if the server was off when a slot passed -- daily/interval backups
don't need replay logic.") but the code didn't implement it.

Fix: in Start(), pre-populate _firedToday with all times <= now so the
wake loop only fires future slots for the rest of today.

Tested live on glados: switched schedule to clock-aligned daily-times
after deploying the fix, zero catch-up backups fired in the 90s window
after restart (vs 5+ before the fix).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 19:31:32 +00:00
Matt 9f0c9e262a pack: content + storage batch v0.24.0 -> v0.26.0
Mods added (in lockfile):
  v0.24.0  YUNG's structure mods (9):
           - Better Dungeons, Better Mineshafts, Better Ocean
             Monuments, Better Nether Fortresses, Better End Island,
             Better Witch Huts, Better Desert Temples, Better Jungle
             Temples, Bridges, Extras
  v0.24.1  Create: Radars (Aeronautics-compatible from v0.4.5.1+)
  v0.24.2  Ars Creo + Create Ars Compact (the latter removed in 0.25.0)
  v0.24.3  FallingTree (whole-tree chop on axe hit)
  v0.24.4  Copycats+, Create: Connected, Create: Interiors
  v0.24.5  Create: Power Loader (Aeronautics-airship chunkloading)
  v0.24.6  -- KubeJS bridges only (see below)
  v0.24.7  -- Patchouli welcome book attempt 1 (kubejs/data) -- failed
  v0.24.8  -- Patchouli attempt 2 (assets/data split) -- failed
  v0.24.9  -- Patchouli attempt 3 (patchouli_books/ root path) -- worked
  v0.25.0  REMOVED Create Ars Compact (broken sauce:source_fluid recipes,
           no public tracker, half-finished compat layer);
           added Create: Dragons Plus, Create: Enchantment Industry
  v0.26.0  Sophisticated Core/Storage/Backpacks/Create-Integration,
           Create: Additional Logistics

KubeJS additions:
  - supplementaries_bridges.js: flax -> 2x string via Create Press,
    Plunderer drops 1-3 spurs via EntityEvents.drops
  - recipes_numismatics.js: renamed COIN_IDS -> COIN_RECIPE_REMOVE_IDS
    to avoid Rhino global-scope collision with numismatics_tooltips.js,
    added sprocket to the removal list (was missing)
  - welcome.js: migrated from vanilla written_book (broken Filterable
    codec in 1.21) to Patchouli book at patchouli_books/manual/

Patchouli manual:
  - 5 entries (Welcome, Waystones, Money, Plots, Rules) under one
    "Server Basics" category
  - Lives at pack/overrides/patchouli_books/ (root path) -- this is
    the modpack-author path per Patchouli docs, NOT data/+assets/
    which is for mods bundled in jars
  - Book id: patchouli:manual (default namespace in this mode)

All v0.24-v0.26 work has been live on the server and tested.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 22:42:32 +00:00
14 changed files with 878 additions and 43 deletions
@@ -0,0 +1,342 @@
#.
#These values affect the extent of cannon failure.
[failure]
#.
#.
#If true, cannons cannot fail whatsoever. Equivalent to setting all the chances below to zero.
disableAllCannonFailure = true
#.
#Chance that a fired projectile will get stuck in a barrel after exceeding the squib ratio. 0 is 0%, 1 is 100%.
# Default: 0.25
# Range: 0.0 ~ 1.0
squibChance = 0.25
#.
#Chance that a cannon will fail if a Powder Charge is ignited in a "barrel"-type cannon block. 0 is 0%, 1 is 100%.
#This chance can be affected by stronger and weaker charge blocks.
# Default: 0.20000000298023224
# Range: 0.0 ~ 1.0
barrelChargeBurstChance = 0.20000000298023224
#.
#How strong the explosion of a catastrophic failure is. Scaled by the amount of charges used in the shot.
# Default: 2.0
# Range: 0.0 ~ 3.4028234663852886E38
failureExplosionPower = 2.0
#.
#Chance that a cannon loaded with more Powder Charges that it can handle will fail. 0 is 0%, 1 is 100%.
# Default: 0.5
# Range: 0.0 ~ 1.0
overloadBurstChance = 0.5
#.
#Chance that a load with gaps between the loaded Powder Charges will not completely combust. 0 is 0%, 1 is 100%.
# Default: 0.33000001311302185
# Range: 0.0 ~ 1.0
interruptedIgnitionChance = 0.33000001311302185
#.
#These values affect the characteristics of cannon munitions.
[munitions]
#.
#.
#If projectiles can bounce, ricochet, and be deflected.
projectilesCanBounce = true
#.
# Default: 0.33000001311302185
# Range: 0.0 ~ 1.0
baseProjectileBounceChance = 0.33000001311302185
#.
# Default: 0.8999999761581421
# Range: 0.0 ~ 1.0
baseProjectileFluidBounceChance = 0.8999999761581421
#.
#[in Meters per Tick]
#The minimum velocity necessary to activate the penetration bonus.
# Default: 1.0
# Range: 0.0 ~ 3.4028234663852886E38
minimumVelocityForPenetrationBonus = 1.0
#.
# Default: 0.10000000149011612
# Range: 0.0 ~ 3.4028234663852886E38
penetrationBonusScale = 0.10000000149011612
#.
#The extent to which cannon projectiles can damage surrounding blocks.
#All Damage - projectiles will destroy anything they hit, if applicable. Explosive projectiles will destroy blocks on detonation.
#No Explosive Damage - projectiles will destroy anything they hit, if applicable. Explosive projectiles will only harm entities on detonation.
#No Damage - projectiles will not destroy anything they hit, and will only deal entity damage. Explosive projectiles will only harm entities on detonation.
#Allowed Values: ALL_DAMAGE, NO_EXPLOSIVE_DAMAGE, NO_DAMAGE
damageRestriction = "ALL_DAMAGE"
#.
projectilesChangeSurroundings = true
#.
munitionBlocksCanExplode = true
#.
#Damp Big Cannon Propellant
[munitions.dampBigCannonPropellant]
#.
dampPropellantBlocksStartingIgnition = true
#.
dampPropellantWeakensPropellant = true
#.
#Chunkloading
[munitions.chunkloading]
#.
projectilesCanChunkload = true
#.
smokeCloudsCanChunkload = true
#.
#Big Cannon Munitions
[munitions.bigCannonMunitions]
#.
#Allowed Values: NONE, LONG, SHORT
trailType = "SHORT"
#.
#Makes all shot big cannon projectiles tracers regardless if the item had a tracer tip applied.
allBigCannonProjectilesAreTracers = false
#.
#[in Ticks]
# Default: 20
# Range: 0 ~ 100
quickFiringBreechItemPickupDelay = 20
#.
quickFiringBreechItemGoesToInventory = false
#.
#Projectile Fuzes
[munitions.fuzes]
#.
#Chance that the Impact Fuze/Delayed Impact Fuze will detonate on hitting something. 0 is 0% (never), 1 is 100% (always).
# Default: 0.6700000166893005
# Range: 0.0 ~ 1.0
impactFuzeDetonationChance = 0.6700000166893005
#.
#How many blocks the Impact Fuze/Delayed Impact Fuze can hit before breaking. Set to -1 to never break.
# Default: 3
# Range: > -1
impactFuzeDurability = 3
#.
#Chance that the Inertia Fuze/Delayed Inertia Fuze will detonate on hitting something. 0 is 0% (never), 1 is 100% (always).
# Default: 0.8999999761581421
# Range: 0.0 ~ 1.0
inertiaFuzeDetonationChance = 0.8999999761581421
#.
#How many blocks the Inertia Fuze/Delayed Inertia Fuze can hit before breaking. Set to -1 to never break.
# Default: 3
# Range: > -1
inertiaFuzeDurability = 3
#.
#[in Ticks]
#Time it takes for a proximity fuze to arm itself.
#After the fuze has been in the air for the specified arming time, it will detonate when it gets close enough to a block or entity.
# Default: 5
# Range: > 0
proximityFuzeArmingTime = 5
#.
#Scale of the area covered by the Proximity Fuze. Larger number means wider area covered
# Default: 5
# Range: 1 ~ 10
proximityFuzeScale = 5
#.
#[in Blocks]
#Spacing of the detection points of the Proximity Fuze.
# Default: 1.5
# Range: 0.5 ~ 2.0
proximityFuzeSpacing = 1.5
#.
#Grouped Munitions
[munitions.groupedMunitions]
#.
#The chance of a fluid blob affecting a block in its area of effect (AOE). 0 is 0% (never), 1 is 100% (always).
# Default: 0.5
# Range: 0.0 ~ 1.0
fluidBlobBlockEffectChance = 0.5
#.
#Autocannon Munitions
[munitions.autocannonMunitions]
#.
#Allowed Values: NONE, LONG, SHORT
trailType = "SHORT"
#.
#Makes all shot autocannon projectiles tracers regardless if the item had a tracer tip applied. Emulates legacy behavior.
allAutocannonProjectilesAreTracers = false
#.
#How many autocannon rounds the Autocannon Ammo Container can store.
# Default: 16
# Range: 1 ~ 128
autocannonAmmoContainerAutocannonRoundCapacity = 16
#.
#How many machine gun rounds the Autocannon Ammo Container can store.
# Default: 64
# Range: 1 ~ 128
autocannonAmmoContainerMachineGunRoundCapacity = 64
#.
#These values affect the characteristics of cannon materials and cannon structures
[cannons]
#.
#.
#Maximum length of cannons that can be built.
# Default: 64
# Range: > 3
maxCannonLength = 64
#.
#Time when the Quickfiring Breech cannot be loaded by Mechanical Arms.
# Default: 40
# Range: > 0
quickfiringBreechLoadingCooldown = 40
#.
#Time it takes for the Quickfiring Breech to fully open/close, during which it cannot be loaded.
# Default: 5
# Range: > 0
quickfiringBreechOpeningCooldown = 5
#.
# Default: 4.0
# Range: 0.0 ~ 3.4028234663852886E38
bigCannonRecoilScale = 4.0
#.
# Default: 0.5
# Range: 0.0 ~ 3.4028234663852886E38
autocannonRecoilScale = 0.5
#.
#Drop Mortar
[cannons.dropMortar]
#.
#Time in ticks between inserting a munition into a drop mortar and the drop mortar firing it. There are 20 ticks in 1 second.
# Default: 5
# Range: > 0
dropMortarDelay = 5
#.
#Length in ticks that the player has to wait for before inserting the same munition type into a drop mortar again. There are 20 ticks in 1 second.
# Default: 40
# Range: > 0
dropMortarItemCooldown = 40
#.
#Loading Tools
[cannons.loadingTools]
#.
#If deployers can use loading tools.
deployersCanUseLoadingTools = false
#.
#How many blocks inside a cannon a Ram Rod can reach.
# Default: 5
# Range: > 1
ramRodReach = 5
#.
#Maximum amount of munition blocks a Ram Rod can push.
# Default: 3
# Range: > 1
ramRodStrength = 3
#.
#How many blocks inside a cannon a Worm can reach.
# Default: 5
# Range: > 1
wormReach = 5
#.
#How many hunger/saturation points it takes to move one munition block a distance of one block using loading tools.
# Default: 2.5
# Range: 0.0 ~ 3.4028234663852886E38
loadingToolHungerConsumption = 2.5
#.
#How many ticks before a player can use a manual loading tool again. There are 20 ticks in 1 second.
# Default: 20
# Range: > 0
loadingToolCooldown = 20
#.
#Cannon Carriages
[cannons.carriage]
#.
#How fast the carriage is, in blocks per tick.
# Default: 0.03999999910593033
# Range: 0.03999999910593033 ~ 1.0
carriageSpeed = 0.03999999910593033
#.
#How fast the carriage turns, in degrees per tick.
# Default: 1.0
# Range: 0.10000000149011612 ~ 10.0
carriageTurnRate = 1.0
#.
cannonWeightAffectsCarriageSpeed = true
#.
#Display Link Info
[cannons.displayLink]
#.
shouldDisplayCannonRotation = true
#.
shouldDisplayContainedMunitions = true
#.
#Big Cannon Screen Shake
[cannons.bigCannonScreenShake]
#.
# Default: 8.0
# Range: 0.0 ~ 3.4028234663852886E38
blastDistanceMultiplier = 8.0
#.
#These values affect various miscellaneous contraptions.
[kinetics]
#.
#.
#Maximum length of cannon loaders that can be built.
# Default: 64
# Range: > 3
maxLoaderLength = 64
#.
#When enabled, Cannon Loaders will not break if another contraption is placed on them. Enables legacy behavior.
enableIntersectionLoading = false
#.
#These values affect the stress of Create Big Cannons' mechanical blocks.
[kinetics.stressValues.v2]
#.
#.
#[in Stress Units]
#Configure the individual stress impact of mechanical blocks. Note that this cost is doubled for every speed increase it receives.
[kinetics.stressValues.v2.impact]
steel_sliding_breech = 32.0
cannon_drill = 8.0
nethersteel_screw_breech = 40.0
steel_screw_breech = 16.0
bronze_sliding_breech = 12.0
cannon_loader = 4.0
cannon_builder = 8.0
cast_iron_sliding_breech = 16.0
#.
#These values affect cannon crafting properties.
[crafting]
#.
#.
#Maximum height of a single cannon cast that can be built.
# Default: 32
# Range: > 1
maxCannonCastHeight = 32
#.
#Maximum length of a Cannon Drill that can be built.
# Default: 32
# Range: > 1
maxCannonDrillLength = 32
#.
#Maximum length of a Cannon Builder that can be built.
# Default: 32
# Range: > 1
maxCannonBuilderLength = 32
#.
#Maximum reach of a Cannon Builder.
# Default: 32
# Range: > 2
maxCannonBuilderRange = 32
#.
#[in Ticks]
#Time a built-up cannon block needs to be heated for until it transforms into its finished form.
# Default: 6000
# Range: > 0
builtUpCannonHeatingTime = 6000
@@ -10,15 +10,18 @@
// depositors, bank cards) stay craftable -- those are infrastructure, // depositors, bank cards) stay craftable -- those are infrastructure,
// not currency. // not currency.
const COIN_IDS = [ // Renamed to avoid collision with COIN_IDS in numismatics_tooltips.js
// (KubeJS server_scripts share a single Rhino global scope).
const COIN_RECIPE_REMOVE_IDS = [
'numismatics:spur', 'numismatics:spur',
'numismatics:bevel', 'numismatics:bevel',
'numismatics:sprocket',
'numismatics:cog', 'numismatics:cog',
'numismatics:crown', 'numismatics:crown',
'numismatics:sun', 'numismatics:sun',
]; ];
ServerEvents.recipes(event => { ServerEvents.recipes(event => {
COIN_IDS.forEach(coin => event.remove({ output: coin })); COIN_RECIPE_REMOVE_IDS.forEach(coin => event.remove({ output: coin }));
console.info(`[bns/recipes_numismatics] removed crafting recipes for ${COIN_IDS.length} coin denominations`); console.info(`[bns/recipes_numismatics] removed crafting recipes for ${COIN_RECIPE_REMOVE_IDS.length} coin denominations`);
}); });
@@ -0,0 +1,44 @@
// Brass & Sigil -- Supplementaries cross-mod bridges
//
// Supplementaries already does tag work (c:crops/flax, c:fiber/flax,
// c:drinks/lumisene, etc.) and Create auto-generates Millstone +
// 9x packing recipes from those tags. So most integration is done.
//
// What's left -- two small bridges:
// 1. Flax -> String via Mechanical Press (Create doesn't map
// c:fiber -> string natively; this fills the gap so flax becomes
// a viable string-source for textile builds).
// 2. Plunderer drops 1-3 Numismatics spurs so the hostile NPC pulls
// into the server economy. Plunderers are rare so this is a
// mild bounty, not a farmable currency source.
//
// Lumisene -> Ars Source was on the list but Ars Nouveau 5.11.x's
// sourcelink recipe schema needs verification before we add it.
// Parked for a follow-up.
ServerEvents.recipes(event => {
// 2 flax pressed -> 1 string. Cheaper than wool->4 string but
// requires a Press (kinetic infrastructure) -- balanced.
//
// Uses the kubejs_create mod's builder API (added in pack v0.27.0).
// Before that, the bare event.recipes.create.pressing() signature
// didn't match Create 6.x's recipe shape and we had to use a raw
// event.custom({type: 'create:pressing', ...}) call.
event.recipes.create.pressing('2x minecraft:string', 'supplementaries:flax')
.id('bns:press_flax_to_string');
console.info('[bns/supplementaries_bridges] added flax -> string press recipe');
});
// Plunderer is a hostile mob that raids unprotected chests. Adding
// a small spur bounty makes them a mini-event rather than a pure
// nuisance. Range tuned low: 1-3 spurs = trivial vs. the player
// economy but creates a "go hunt the plunderer" hook.
//
// KubeJS 2101.7.2 doesn't expose ServerEvents.entityLootTables on
// NeoForge -- we use EntityEvents.drops (filtered by entity id) and
// add items via event.addDrop instead.
EntityEvents.drops('supplementaries:plunderer', event => {
const count = 1 + Math.floor(Math.random() * 3);
event.addDrop(Item.of('numismatics:spur', count));
});
console.info('[bns/supplementaries_bridges] registered plunderer -> spur drops handler');
+16 -23
View File
@@ -1,34 +1,27 @@
// Brass & Sigil first-join welcome grant // Brass & Sigil -- first-join welcome grant
// //
// Gives each new player exactly 1 Waystone + 1 server manual book on // Gives each new player exactly 1 Waystone + 1 Patchouli manual on
// first login. Gated by a per-player persistent flag so it fires once // first login. Gated by a per-player persistent flag so it fires once
// even if the player relogs. // even if the player relogs.
// //
// Future: replace the vanilla written book with a Patchouli book in a // The manual is a Patchouli book ("patchouli:manual") shipped in the
// custom tweak jar once we have enough content to warrant the rich // pack's root patchouli_books/ folder (the modpack-author path that
// formatting. Patchouli is already in the pack so swapping later is // Patchouli's docs recommend for non-mod books).
// purely a content+recipe change, no mod-add needed. // Folder layout:
// patchouli_books/manual/book.json
// patchouli_books/manual/en_us/categories/<name>.json
// patchouli_books/manual/en_us/entries/<name>.json
// Namespace defaults to "patchouli" in this mode -- the book id is
// "patchouli:manual".
const WELCOME_FLAG = 'bnsWelcomeGranted'; const WELCOME_FLAG = 'bnsWelcomeGranted';
const MANUAL_PAGES = [
'Welcome to Brass & Sigil.\n\nThis manual covers the basics. Read it once, then keep it or toss it.',
'WAYSTONES\n\nYou have one Waystone item. Place it at your base -- it is the only one you get.\n\nNeed to travel further? Build trains, roads, or Create Aeronautics planes.',
'MONEY\n\nCoins cannot be crafted.\n\nTake valuables (diamonds etc.) to the bank at spawn. The exchange will trade them for coins.\n\nDynamic rates are planned.',
'SPAWN PLOTS\n\nComing soon: buy a small commercial plot at spawn to set up your shop. One plot per player.\n\nYour home base goes OUT in the world -- claim it with FTB Chunks (free, anywhere).',
'RULES\n\n- Be excellent to each other.\n- No griefing.\n- Lost your Waystone? Ask an OP or buy one from the bank (soon).\n- Report bugs to staff.\n\nHave fun.',
];
function giveManual(player) { function giveManual(player) {
// 1.21 components-based written book: pages are filterable text // Patchouli's guide_book uses the "patchouli:book" data component
// components, title supports the same. Plain strings are accepted. // (1.21 component system) to identify which book to open. Plain
const book = Item.of('minecraft:written_book'); // resource location string is the expected value type.
book.set('minecraft:written_book_content', { const book = Item.of('patchouli:guide_book');
title: { raw: 'Brass & Sigil Manual' }, book.set('patchouli:book', 'patchouli:manual');
author: 'Server Staff',
pages: MANUAL_PAGES.map(text => ({ raw: text })),
resolved: true,
});
player.give(book); player.give(book);
} }
@@ -0,0 +1,11 @@
{
"name": "Brass & Sigil Manual",
"landing_text": "Welcome to Brass & Sigil. This manual covers the essentials -- read it once, then keep it or toss it.",
"subtitle": "The Server Handbook",
"creative_tab": "minecraft:tools_and_utilities",
"model": "patchouli:guide_book",
"show_progress": false,
"show_advancements": false,
"book_texture": "patchouli:textures/gui/book_brown.png",
"version": 1
}
@@ -0,0 +1,6 @@
{
"name": "Server Basics",
"description": "The essentials for life in Brass & Sigil.",
"icon": "minecraft:writable_book",
"sortnum": 0
}
@@ -0,0 +1,18 @@
{
"name": "Money",
"icon": "numismatics:cog",
"category": "patchouli:main",
"sortnum": 2,
"pages": [
{
"type": "patchouli:text",
"title": "Coins & Trade",
"text": "Coins $(bold)cannot be crafted$(0). They enter circulation only through the bank at spawn.$(br2)Take valuables -- diamonds, emeralds, rare loot -- to the exchange. It trades them for coins at fixed rates."
},
{
"type": "patchouli:text",
"title": "Denominations",
"text": "$(li)$(item)Spur$() = 1$(li)$(item)Bevel$() = 8 spurs$(li)$(item)Sprocket$() = 16 spurs$(li)$(item)Cog$() = 64 spurs$(li)$(item)Crown$() = 512 spurs$(li)$(item)Sun$() = 4 096 spurs$(br2)Use a $(item)Bank Teller$() block to convert between denominations."
}
]
}
@@ -0,0 +1,18 @@
{
"name": "Plots & Claims",
"icon": "ftbchunks:map",
"category": "patchouli:main",
"sortnum": 3,
"pages": [
{
"type": "patchouli:text",
"title": "Spawn Plots",
"text": "Buy a small commercial plot at spawn to set up your shop or stall. $(bold)One plot per player$(0).$(br2)Plots are limited and rented from the Plot Office at spawn -- pay in $(item)cog$()s, or trade rare items for the upgrade."
},
{
"type": "patchouli:text",
"title": "Your Home Base",
"text": "Your $(bold)home base$(0) lives out in the world -- not on a spawn plot. Pick a spot you like and claim it with $(item)FTB Chunks$(): open the map ($(item)m$()), click chunks to claim and force-load.$(br2)FTB Chunks claims are $(bold)free$(0) and protect against grief from other players."
}
]
}
@@ -0,0 +1,27 @@
{
"name": "Rules",
"icon": "minecraft:shield",
"category": "patchouli:main",
"sortnum": 4,
"pages": [
{
"type": "patchouli:text",
"title": "House Rules",
"text": "$(li)Be excellent to each other.$(li)No griefing.$(li)Don't break others' contraptions or fly through their bases.$(li)Lost your Waystone? Ask an op or buy one from the bank.$(li)Report bugs and crashes to staff."
},
{
"type": "patchouli:text",
"title": "PvP & Combat",
"text": "PvP is $(bold)allowed$(0) outside claimed chunks. Claim your base with $(item)FTB Chunks$() to protect it.$(br2)Airships flying through unclaimed chunks are $(bold)fair game$(0) -- if you take it out beyond your claim, expect cannons."
},
{
"type": "patchouli:text",
"title": "Griefing vs PvP",
"text": "PvP and griefing are $(bold)not the same$(0).$(br2)Allowed: combat, raiding contested ground, sinking each other's airships in open air.$(br2)$(bold)Not allowed -- ban-worthy:$()$(li)Repeated targeted harassment of a player$(li)Destruction of unclaimed builds purely to ruin them$(li)Camping someone's claim border to grief them$(li)Stealing keys/coins via social engineering or exploit$(br2)If unsure, ask staff first."
},
{
"type": "patchouli:text",
"text": "$(bold)Have fun.$(0)$(br2)This manual is a living document -- expect new pages as the server grows. To re-read at any time, hold the manual and right-click."
}
]
}
@@ -0,0 +1,17 @@
{
"name": "Waystones",
"icon": "waystones:waystone",
"category": "patchouli:main",
"sortnum": 1,
"pages": [
{
"type": "patchouli:text",
"title": "Waystones",
"text": "You have one $(item)Waystone$() in your starter kit. $(bold)Place it at your base$() -- it is the only one you get from the server."
},
{
"type": "patchouli:text",
"text": "Need to travel further? Build:$(br)$(li)$(item)Create$() trains and tracks$(li)Roads with the $(item)brass road sign$()$(li)$(item)Create Aeronautics$() planes and airships$(br2)Once Quests are live, additional Waystones will be purchasable at the bank."
}
]
}
@@ -0,0 +1,14 @@
{
"name": "Welcome",
"icon": "minecraft:cake",
"category": "patchouli:main",
"priority": true,
"sortnum": 0,
"pages": [
{
"type": "patchouli:text",
"title": "Welcome",
"text": "Welcome to $(bold)Brass & Sigil$(0)!$(br2)This manual covers the basics. Skim it now, then keep it on you or toss it in a chest -- you can always craft another by combining a $(item)book$() with a server-issued $(item)sigil$()."
}
]
}
+323 -15
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.23.0", "version": "0.28.0",
"minecraft": "1.21.1", "minecraft": "1.21.1",
"loader": { "loader": {
"type": "neoforge", "type": "neoforge",
"version": "21.1.228" "version": "21.1.228"
}, },
"lockedAt": "2026-05-04T13:22:58.7131203+00:00", "lockedAt": "2026-05-28T19:27:42Z",
"mods": [ "mods": [
{ {
"source": "modrinth", "source": "modrinth",
@@ -53,6 +53,17 @@
"size": 3715787, "size": 3715787,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "create-radars",
"versionId": "3MbtX7qD",
"version": "0.4.8-1.21.1",
"path": "mods/create_radar-0.4.8-1.21.1.jar",
"url": "https://cdn.modrinth.com/data/BLu2Yqfq/versions/3MbtX7qD/create_radar-0.4.8-1.21.1.jar",
"sha1": "e33b4957cf831e4ee76c5cb334119270e057c529",
"size": 16462408,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "create-tfmg", "slug": "create-tfmg",
@@ -64,6 +75,83 @@
"size": 4924243, "size": 4924243,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "copycats",
"versionId": "kecZ0sl7",
"version": "3.0.4+mc.1.21.1-neoforge",
"path": "mods/copycats-3.0.4+mc.1.21.1-neoforge.jar",
"url": "https://cdn.modrinth.com/data/UT2M39wf/versions/kecZ0sl7/copycats-3.0.4%2Bmc.1.21.1-neoforge.jar",
"sha1": "6a11d5806bcf521e08faba1ee6ef5f31af55ca0f",
"size": 1791747,
"side": "both"
},
{
"source": "modrinth",
"slug": "create-connected",
"versionId": "EHyCRpCV",
"version": "1.1.16-mc1.21.1",
"path": "mods/create_connected-1.1.16-mc1.21.1.jar",
"url": "https://cdn.modrinth.com/data/Vg5TIO6d/versions/EHyCRpCV/create_connected-1.1.16-mc1.21.1.jar",
"sha1": "11fafde06b25b872f8d5c4e927516188b831cf1a",
"size": 6608605,
"side": "both"
},
{
"source": "modrinth",
"slug": "interiors",
"versionId": "gBrfZy6S",
"version": "0.6.1",
"path": "mods/interiors-1.21.1-neoforge-0.6.1.jar",
"url": "https://cdn.modrinth.com/data/r4Knci2k/versions/gBrfZy6S/interiors-1.21.1-neoforge-0.6.1.jar",
"sha1": "a558777ef4510da2f4d32ca3da6ac7384c9a9940",
"size": 486912,
"side": "both"
},
{
"source": "modrinth",
"slug": "create-power-loader",
"versionId": "3Y4r0ItR",
"version": "2.0.5-mc1.21.1",
"path": "mods/create_power_loader-2.0.5-mc1.21.1.jar",
"url": "https://cdn.modrinth.com/data/wPQ6GgFE/versions/3Y4r0ItR/create_power_loader-2.0.5-mc1.21.1.jar",
"sha1": "5bd3ecef6fc66985e6fdf416706c98fb774ba03d",
"size": 442369,
"side": "both"
},
{
"source": "modrinth",
"slug": "kubejs-create",
"versionId": "1qmcLagN",
"version": "2101.3.1-build.18",
"path": "mods/kubejs-create-neoforge-2101.3.1-build.18.jar",
"url": "https://cdn.modrinth.com/data/T38eAZQC/versions/1qmcLagN/kubejs-create-neoforge-2101.3.1-build.18.jar",
"sha1": "f54d9ff53909771d27e4d9d135c3707cd5d5f855",
"size": 38629,
"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",
"slug": "bellsandwhistles",
"versionId": "w0mifib8",
"version": "v0.4.7-1.21.1",
"path": "mods/bellsandwhistles-0.4.7-1.21.1.jar",
"url": "https://cdn.modrinth.com/data/gJ5afkVv/versions/w0mifib8/bellsandwhistles-0.4.7-1.21.1.jar",
"sha1": "85944080f8bd47dad64a8193a70662cd9ddbb200",
"size": 193619,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "distanthorizons", "slug": "distanthorizons",
@@ -240,6 +328,94 @@
"size": 20689115, "size": 20689115,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "ars-creo",
"versionId": "l9IAhn9x",
"version": "5.3.1",
"path": "mods/ars_creo-1.21.1-5.3.1.jar",
"url": "https://cdn.modrinth.com/data/fZ324GMc/versions/l9IAhn9x/ars_creo-1.21.1-5.3.1.jar",
"sha1": "296ea073e2976360a626b1dc30a392318a0ca522",
"size": 87005,
"side": "both"
},
{
"source": "modrinth",
"slug": "create-dragons-plus",
"versionId": "r0TIh2nX",
"version": "1.10.1",
"path": "mods/CreateDragonsPlus-1.10.1.jar",
"url": "https://cdn.modrinth.com/data/dzb1a5WV/versions/r0TIh2nX/CreateDragonsPlus-1.10.1.jar",
"sha1": "0759898cc7ca22ece49e7d7a210db8becfd79cdf",
"size": 759631,
"side": "both"
},
{
"source": "modrinth",
"slug": "sophisticated-core",
"versionId": "xLeCYw3D",
"version": "1.21.1-1.4.41.1867",
"path": "mods/sophisticatedcore-1.21.1-1.4.41.1867.jar",
"url": "https://cdn.modrinth.com/data/nmoqTijg/versions/xLeCYw3D/sophisticatedcore-1.21.1-1.4.41.1867.jar",
"sha1": "c5880807715abf1cf276be0afdd1351a3cc747e0",
"size": 1607780,
"side": "both"
},
{
"source": "modrinth",
"slug": "sophisticated-storage",
"versionId": "dMp3ZwPO",
"version": "1.21.1-1.5.50.1743",
"path": "mods/sophisticatedstorage-1.21.1-1.5.50.1743.jar",
"url": "https://cdn.modrinth.com/data/hMlaZH8f/versions/dMp3ZwPO/sophisticatedstorage-1.21.1-1.5.50.1743.jar",
"sha1": "aafe9fa35429b42073ba7dac22318aa5b22c83cb",
"size": 1689268,
"side": "both"
},
{
"source": "modrinth",
"slug": "sophisticated-storage-create-integration",
"versionId": "cxMKWFFD",
"version": "1.21.1-0.1.17.132",
"path": "mods/sophisticatedstoragecreateintegration-1.21.1-0.1.17.132.jar",
"url": "https://cdn.modrinth.com/data/MJ0hdevs/versions/cxMKWFFD/sophisticatedstoragecreateintegration-1.21.1-0.1.17.132.jar",
"sha1": "afabee69117b0cd50184579af33d80d096f4a9c6",
"size": 133451,
"side": "both"
},
{
"source": "modrinth",
"slug": "sophisticated-backpacks",
"versionId": "tqDekk0k",
"version": "1.21.1-3.25.49.1788",
"path": "mods/sophisticatedbackpacks-1.21.1-3.25.49.1788.jar",
"url": "https://cdn.modrinth.com/data/TyCTlI4b/versions/tqDekk0k/sophisticatedbackpacks-1.21.1-3.25.49.1788.jar",
"sha1": "bf46106f0e2df9a9749891735440c357550a3d4a",
"size": 1056463,
"side": "both"
},
{
"source": "modrinth",
"slug": "create-additional-logistics",
"versionId": "soesZiME",
"version": "1.4.5",
"path": "mods/createadditionallogistics-1.21.1-1.4.5.jar",
"url": "https://cdn.modrinth.com/data/CZaz7aje/versions/soesZiME/createadditionallogistics-1.21.1-1.4.5.jar",
"sha1": "6acf4d30516b6bf66928aa5d79d6bac6283de44e",
"size": 953230,
"side": "both"
},
{
"source": "modrinth",
"slug": "create-enchantment-industry",
"versionId": "zjSKnkVT",
"version": "2.3.1",
"path": "mods/create-enchantment-industry-2.3.1.jar",
"url": "https://cdn.modrinth.com/data/JWGBpFUP/versions/zjSKnkVT/create-enchantment-industry-2.3.1.jar",
"sha1": "e9a57b322459bda9905e4287d2582ad600909b11",
"size": 684729,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "terralith", "slug": "terralith",
@@ -262,6 +438,116 @@
"size": 461244, "size": 461244,
"side": "server" "side": "server"
}, },
{
"source": "modrinth",
"slug": "yungs-better-dungeons",
"versionId": "D6aZn0Em",
"version": "1.21.1-NeoForge-5.1.4",
"path": "mods/YungsBetterDungeons-1.21.1-NeoForge-5.1.4.jar",
"url": "https://cdn.modrinth.com/data/o1C1Dkj5/versions/D6aZn0Em/YungsBetterDungeons-1.21.1-NeoForge-5.1.4.jar",
"sha1": "73a660c13063450e555e5e2c015ae24d606b32b4",
"size": 782160,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-mineshafts",
"versionId": "Go3nbneL",
"version": "1.21.1-NeoForge-5.1.1",
"path": "mods/YungsBetterMineshafts-1.21.1-NeoForge-5.1.1.jar",
"url": "https://cdn.modrinth.com/data/HjmxVlSr/versions/Go3nbneL/YungsBetterMineshafts-1.21.1-NeoForge-5.1.1.jar",
"sha1": "6a14e1d8201f068ca87ef8333e471c5378955906",
"size": 493967,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-ocean-monuments",
"versionId": "yFjEcj2g",
"version": "1.21.1-NeoForge-4.1.2",
"path": "mods/YungsBetterOceanMonuments-1.21.1-NeoForge-4.1.2.jar",
"url": "https://cdn.modrinth.com/data/3dT9sgt4/versions/yFjEcj2g/YungsBetterOceanMonuments-1.21.1-NeoForge-4.1.2.jar",
"sha1": "fba629defdb92ad99ec4f411895afb6e19555a18",
"size": 1025571,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-nether-fortresses",
"versionId": "iopJiJQp",
"version": "1.21.1-NeoForge-3.1.5",
"path": "mods/YungsBetterNetherFortresses-1.21.1-NeoForge-3.1.5.jar",
"url": "https://cdn.modrinth.com/data/Z2mXHnxP/versions/iopJiJQp/YungsBetterNetherFortresses-1.21.1-NeoForge-3.1.5.jar",
"sha1": "4061ece2141475227646649046fc14c2da505922",
"size": 609639,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-end-island",
"versionId": "I52NZ1qK",
"version": "1.21.1-NeoForge-3.1.2",
"path": "mods/YungsBetterEndIsland-1.21.1-NeoForge-3.1.2.jar",
"url": "https://cdn.modrinth.com/data/2BwBOmBQ/versions/I52NZ1qK/YungsBetterEndIsland-1.21.1-NeoForge-3.1.2.jar",
"sha1": "832f2c17425debe74a9f267f4136f1a0f0221d19",
"size": 398130,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-witch-huts",
"versionId": "AvedwcIe",
"version": "1.21.1-NeoForge-4.1.1",
"path": "mods/YungsBetterWitchHuts-1.21.1-NeoForge-4.1.1.jar",
"url": "https://cdn.modrinth.com/data/t5FRdP87/versions/AvedwcIe/YungsBetterWitchHuts-1.21.1-NeoForge-4.1.1.jar",
"sha1": "bbf6c91f4a2829c58bc024eaaab821986bc93de6",
"size": 174789,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-desert-temples",
"versionId": "GQ9iNWkI",
"version": "1.21.1-NeoForge-4.1.5",
"path": "mods/YungsBetterDesertTemples-1.21.1-NeoForge-4.1.5.jar",
"url": "https://cdn.modrinth.com/data/XNlO7sBv/versions/GQ9iNWkI/YungsBetterDesertTemples-1.21.1-NeoForge-4.1.5.jar",
"sha1": "90529257dbd92558998c65178294bc7b2fd16a64",
"size": 905839,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-better-jungle-temples",
"versionId": "P00i2hJn",
"version": "1.21.1-NeoForge-3.1.2",
"path": "mods/YungsBetterJungleTemples-1.21.1-NeoForge-3.1.2.jar",
"url": "https://cdn.modrinth.com/data/z9Ve58Ih/versions/P00i2hJn/YungsBetterJungleTemples-1.21.1-NeoForge-3.1.2.jar",
"sha1": "d6b7ce6cf351b09cbd23147ff166c35cfdc572e8",
"size": 795839,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-bridges",
"versionId": "urkCzBf6",
"version": "1.21.1-NeoForge-5.1.1",
"path": "mods/YungsBridges-1.21.1-NeoForge-5.1.1.jar",
"url": "https://cdn.modrinth.com/data/Ht4BfYp6/versions/urkCzBf6/YungsBridges-1.21.1-NeoForge-5.1.1.jar",
"sha1": "18099c2d54acd16bb8ab90a66f40fa16496d065e",
"size": 136820,
"side": "server"
},
{
"source": "modrinth",
"slug": "yungs-extras",
"versionId": "N2EpMhR7",
"version": "1.21.1-NeoForge-5.1.1",
"path": "mods/YungsExtras-1.21.1-NeoForge-5.1.1.jar",
"url": "https://cdn.modrinth.com/data/ZYgyPyfq/versions/N2EpMhR7/YungsExtras-1.21.1-NeoForge-5.1.1.jar",
"sha1": "62833733aaf407f36bdacf648ed4b2ef9940662b",
"size": 198183,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "lithostitched", "slug": "lithostitched",
@@ -588,7 +874,7 @@
"version": "2.3.2", "version": "2.3.2",
"path": "mods/mcw-roofs-2.3.2-mc1.21.1neoforge.jar", "path": "mods/mcw-roofs-2.3.2-mc1.21.1neoforge.jar",
"url": "https://cdn.modrinth.com/data/B8jaH3P1/versions/jiXRXiSt/mcw-roofs-2.3.2-mc1.21.1neoforge.jar", "url": "https://cdn.modrinth.com/data/B8jaH3P1/versions/jiXRXiSt/mcw-roofs-2.3.2-mc1.21.1neoforge.jar",
"sha1": "05806ff152e739ea0c5db86dabd9caf0cfa55b9d", "sha1": "05806ff152fab56f7c1b4b215d772ea73611283d",
"size": 1784829, "size": 1784829,
"side": "both" "side": "both"
}, },
@@ -599,7 +885,7 @@
"version": "1.1.1", "version": "1.1.1",
"path": "mods/mcw-mcwpaths-1.1.1-mc1.21.1neoforge.jar", "path": "mods/mcw-mcwpaths-1.1.1-mc1.21.1neoforge.jar",
"url": "https://cdn.modrinth.com/data/VRLhWB91/versions/tlymsxUG/mcw-mcwpaths-1.1.1-mc1.21.1neoforge.jar", "url": "https://cdn.modrinth.com/data/VRLhWB91/versions/tlymsxUG/mcw-mcwpaths-1.1.1-mc1.21.1neoforge.jar",
"sha1": "4bb7e1d28e8b3b5d3a3eaf5e0d3bcc62d1ab57f1", "sha1": "4bb7e1d28eef19f87438881b0f582a15297b95ae",
"size": 777742, "size": 777742,
"side": "both" "side": "both"
}, },
@@ -658,17 +944,6 @@
"size": 1190404, "size": 1190404,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "create-deco",
"versionId": "qrcMVoBD",
"version": "2.1.3",
"path": "mods/createdeco-2.1.3.jar",
"url": "https://cdn.modrinth.com/data/sMvUb4Rb/versions/qrcMVoBD/createdeco-2.1.3.jar",
"sha1": "93d9eff58b46c6953e907d071e6be4e3ce07be38",
"size": 3328498,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "effortless-building", "slug": "effortless-building",
@@ -680,6 +955,17 @@
"size": 810119, "size": 810119,
"side": "both" "side": "both"
}, },
{
"source": "modrinth",
"slug": "fallingtree",
"versionId": "wxGXaJMA",
"version": "1.21.1-1.21.1.11",
"path": "mods/FallingTree-1.21.1-1.21.1.11.jar",
"url": "https://cdn.modrinth.com/data/Fb4jn8m6/versions/wxGXaJMA/FallingTree-1.21.1-1.21.1.11.jar",
"sha1": "7e1b3ee0ba89b556d5a468797bb1b5f4e2b8ccb7",
"size": 496356,
"side": "both"
},
{ {
"source": "modrinth", "source": "modrinth",
"slug": "moonlight", "slug": "moonlight",
@@ -690,6 +976,28 @@
"sha1": "a865d9a00a595038d35b908c42565930189be68a", "sha1": "a865d9a00a595038d35b908c42565930189be68a",
"size": 1578765, "size": 1578765,
"side": "both" "side": "both"
},
{
"source": "modrinth",
"slug": "create-steam-n-rails-1.21.1",
"versionId": "oQKsMKfj",
"version": "0.2.0+neoforge-mc1.21.1",
"path": "mods/railways-0.2.0+neoforge-mc1.21.1.jar",
"url": "https://cdn.modrinth.com/data/L3Jv0QZI/versions/oQKsMKfj/railways-0.2.0%2Bneoforge-mc1.21.1.jar",
"sha1": "9d52eef0772b81de397271b98c0201a59733f9c6",
"size": 12106875,
"side": "both"
},
{
"source": "modrinth",
"slug": "copycats+-aeronautics-weight",
"versionId": "wsXjRa7l",
"version": "1.1.1",
"path": "mods/aerocopycats-1.1.1.jar",
"url": "https://cdn.modrinth.com/data/wjpmYU1u/versions/wsXjRa7l/aerocopycats-1.1.1.jar",
"sha1": "3a93862421d0a8cdae1e3a3d7f0d4bd01e049007",
"size": 14702,
"side": "both"
} }
], ],
"defaultServer": { "defaultServer": {
+18 -1
View File
@@ -37,11 +37,28 @@ public sealed class BackupScheduler : IDisposable
public void Start() public void Start()
{ {
if (string.IsNullOrWhiteSpace(_config.BackupSchedule)) return; if (string.IsNullOrWhiteSpace(_config.BackupSchedule)) return;
if (Parse(_config.BackupSchedule) == default) var parsed = Parse(_config.BackupSchedule);
if (parsed == default)
{ {
_log($"[backup-scheduler] Invalid backupSchedule '{_config.BackupSchedule}'. Expected 'HH:mm', 'HH:mm,HH:mm', or 'every Nh'/'every Nm'. Disabled."); _log($"[backup-scheduler] Invalid backupSchedule '{_config.BackupSchedule}'. Expected 'HH:mm', 'HH:mm,HH:mm', or 'every Nh'/'every Nm'. Disabled.");
return; return;
} }
// No-replay startup: mark all of today's already-passed slots as fired so
// the wake loop only fires future slots. Without this, a daily-times
// schedule like "00:00,04:00,...,20:00" on a service restart at 19:00
// would catch up by firing 5 backups at 1-minute intervals (one per
// missed slot), each producing a full-world zip of the SAME world state.
// For full-snapshot backups that's pure waste -- the live world hasn't
// diverged across missed slots.
if (parsed.Times is not null)
{
var nowTime = TimeOnly.FromDateTime(DateTime.Now);
_lastFireDay = DateOnly.FromDateTime(DateTime.Now);
foreach (var t in parsed.Times)
if (t <= nowTime) _firedToday.Add(t);
}
_cts?.Cancel(); _cts?.Cancel();
_cts = new CancellationTokenSource(); _cts = new CancellationTokenSource();
_loop = Task.Run(() => RunAsync(_cts.Token)); _loop = Task.Run(() => RunAsync(_cts.Token));
+17
View File
@@ -0,0 +1,17 @@
# ZFS ARC sizing for glados — a mixed-workload host (32 GB RAM).
#
# Why these numbers:
# - The cold ZFS pool is BACKUP TIER ONLY. Hot data (MC world, Gitea repos,
# Directus DB, containers) all live on NVMe LVM, not ZFS — so a large
# ARC doesn't help any of the actual workloads.
# - Tier-2 backup work is streaming writes (rsync hot→cold every 4h):
# streaming writes don't benefit much from cache.
# - Reads happen rarely (disaster recovery, Gitea LFS fetches), and a
# one-time cold read is fine.
# - MC is the memory-hungry workload (10 GB Java heap). Every GB we DON'T
# give to ARC is a GB more headroom for MC and future content.
#
# 2 GB max / 1 GB min keeps ZFS metadata + small ARC for occasional reads,
# without crowding MC out of RAM.
options zfs zfs_arc_max=2147483648
options zfs zfs_arc_min=1073741824