Compatibility
Minecraft: Java Edition
Platforms
Supported environments
Tags
Creators
Details
Sable: Destructive
Sable: Destructive is a NeoForge 1.21.1 add-on for Sable that turns physics blocks into something that can actually be destroyed. Crash a ship into the ground, set off a creeper next to a flying base, or drop an end crystal on a tank — Sable sub-levels now react to violence the way they should: chunks fly off, weak materials pulverize, and reinforced ones hold.
What it does
Out of the box, Sable gives you beautifully simulated physics blocks, but they are effectively indestructible. This add-on plugs directly into Sable's collision and explosion callbacks and decides, on every meaningful impact, whether the affected blocks should:
- DETACH — break off into a smaller, independent physics sub-level (default ~90% of impacts). The piece keeps flying, tumbling, and colliding on its own.
- VANISH — pulverize into particles + sound, with a fancy multi-layer effect (default ~10% of impacts). Used when the energy is high enough to truly shatter the material.
The choice between the two is not a flat dice roll. It is biased by real physics:
- Kinetic energy at the contact point (mass × v²) vs. material toughness.
- Block brittleness (leaves and grass shatter; obsidian and netherite resist).
- A safety speed ceiling so absurdly fast impacts don't spawn endless sub-levels.
You see the underlying numbers right in-game with F3 + H:
Mass: 540 kg
Impact strength: 20 m/s
Features
- ✅ Full impact handling for all 1.21.1 blocks, with a sensible mass + toughness table.
- ✅ Modded blocks default to the fragile end (clay-tier) so unknown blocks don't become indestructible bricks.
- ✅ Vanilla explosions are routed through the same path: TNT, creepers, end crystals, beds-in-the-Nether all damage Sable sub-levels.
- ✅ World terrain is also affected, but only via VANISH (never DETACH), so the world never turns into a runaway physics sim. There's a config flag to restrict damage to sub-level blocks only.
- ✅ Fancy VANISH effect: 3-layer particles (block-dust with the actual block texture, POOF, CRIT chips) + block break sound + soft "whump", with hard per-tick budgets so 500 simultaneous breaks don't tank your FPS.
- ✅ Crash-safe: every native call into Sable is wrapped, deferred to safe inter-tick windows, and guarded against
NaN/Inf, mid-step mutation, and cross-parent cluster errors. Years of "Minecraft just closed without a log" — fixed. - ✅ Cluster detach: blocks can break off in chunks, not strictly one at a time, for cinematic damage.
- ✅ Runtime command system — no restart, no config file editing required.
- ✅ Persistent config (since 1.2.0) at
config/sabledestructive-common.toml. Every value set via/sable-dv set …survives restarts, and editing the TOML on disk hot-reloads within ~250 ms. - ✅ Hand-tuned block table (since 1.2.0): ~70 of the most relevant 1.21.1 blocks — every wood species and the full stone family — carry their own mass and toughness instead of being lumped into one "wood" or "stone" bucket. Cherry is softer than oak. Mossy bricks crack a hair sooner than fresh ones. Slabs/stairs/walls/fences/doors/trapdoors/signs each get their own row. Other vanilla blocks continue to use the existing classification pipeline.
- ✅ Inertial penetration (since 1.1.0): impacts no longer stop at the first block. The attacker's kinetic energy is drained block-by-block along the motion direction by each consumed block's break cost (
0.5 · m · v_thr²). An obsidian hammer punches a deep crater into dirt and only chips the surface of stone, automatically. - ✅ Real damage (since 1.2.0): default damage roughly doubled, speed scales penetration depth and energy budget, and density actually matters — soft attackers no longer pretend to break hard surfaces.
- ✅ Self-damage (since 1.2.0): when the attacker is much softer than the defender, the defender survives AND the attacker erodes its own contact face. Copper hammer vs obsidian wall: copper sheds, wall stands.
- ✅ Real sub-level mass (1.6.0): the break decision and shockwave magnitude now read each sub-level's actual mass (kg) straight from Sable's own
ServerSubLevel#getMassTracker()— the same number Rapier uses for the physics body. Pre-1.6.0 every attacker was hard-coded to 2 t for break purposes, so a 100 kg flying chunk and a 500 t fortress felt identical. Now a fortress crushes obsidian from inertia while a thrown cobble bounces off stone. The structural-toughness gate also gets asqrt(refMass / realMass)relief factor, floored at 10×, so a 100 t crusher made of packed dirt pulverises stone but a Jell-O sub-level at any mass still can't peel bedrock. - ✅ Shockwave (rebuilt in 1.5.0, mass-driven in 1.6.0): only fires for genuinely substantial contacts. Magnitude is now driven by the real sub-level mass × an average-toughness constant (replacing the old 6-neighbour flood-fill, which capped at 1500 blocks and underestimated big ships). One radial dust burst plus a tall, slowly-rising semi-transparent dust column (mushroom shape, ~25% alpha, settles over 2–3 s) plus one heavy thud, with per-source dedup and a per-tick particle budget. Radius and toughness ceiling both scale with the cube root of magnitude — small waves rip leaves, giant waves carve a real crater. In DETACHING mode the wave still emits visuals and sound but only damages foliage.
- ✅ Universal toughness gates (1.5.0): every mode, DETACHING included, now applies four hard gates before any break can happen — infinite toughness (bedrock, barrier, command block, end portal frame, structure block) is rejected, vanilla
getDestroySpeed < 0is rejected, attacker-vs-defender toughness ratio is enforced (a 20-unit attacker cannot break a 120-unit defender no matter how fast it goes — it erodes its own contact face instead), and a raw speed-vs-breakSpeedMsgate filters out trivial bumps. Bedrock inside a sub-level is now actually unbreakable. - ✅ Spherical cluster detach (1.5.0): the cluster planner used to grow with a FIFO BFS, which on flat single-layer hulls produced 6-block-long strips along the surface. The new planner uses a distance-ordered priority queue and a hard Chebyshev radius cap of
ceil(cbrt(targetSize))around the seed, so clusters stay roughly spherical even on thin plating and a single-block contact can never spawn a line. - ✅ Localized tooltip (1.5.0): the F3+H
Toughness: N units/Toughness: indestructibleline uses translation keys and ships with English, Russian, German, Spanish, French, Portuguese (Brazil), Japanese, and Simplified Chinese. - ✅ Dot-product incidence (since 1.4.0): glancing blows actually glance. Damage scales with
(v · n)² / |v|²— a sideways scrape on a wall deals a fraction of a head-on hit at the same speed, with a configurable floor so it never reaches zero. - ✅ Collision modes (since 1.4.0, cleaned up in 1.5.0): one command swaps the entire collision behaviour. Three canonical modes:
/sable-dv mode delete— destruction only (VANISH, no new sub-levels spawn)./sable-dv mode detaching— default. Clean chunks peel off, nothing structural is silently deleted, world terrain is never touched, the shockwave only damages foliage./sable-dv mode delete-detaching— combo: seed peels off AND the inertial chain chews extra blocks deeper into the strike. Persistent across restarts.
Commands
All commands require op level 2.
/sable-dv on enable the system
/sable-dv off disable the system
/sable-dv toggle flip enabled
/sable-dv status current enabled state
/sable-dv mode show current collision mode
/sable-dv mode delete blocks only get destroyed
/sable-dv mode detaching default — clean chunks peel off
/sable-dv mode delete-detaching combo: seed detaches + chain breaks
/sable-dv config list list every tunable + current value
/sable-dv config get <name> read one value
/sable-dv config set <name> <value>
/sable-dv config reset <name>
Tunables include chanceDetach, chanceVanish, physicsBiasStrength, minBreakSpeed, absoluteMaxSpeed, detachSafeSpeedCeiling, maxDetachesPerSecond, maxActiveDetachedSubLevels, effects, fancyVanishEffect, affectOnlySubLevelBlocks, and many more.
Performance
Designed to stay quiet when nothing is happening, and not to fall over when everything is happening:
- Lock-free offer queue with atomic size counter and epoch-XOR dedup ring (8192 entries).
- Per-tick caps: 1024 offers, 600 vanish particles, 32 vanish effects.
- Spatial + temporal dedup on effects so big breaks don't audio-clip.
- Auto-scaling under backpressure; falls back to vanilla
levelEvent(2001)if the fancy pipeline is overloaded. - DETACH is always deferred to a safe tick boundary — never executed while Sable's physics system is mid-step.
Requirements
- Minecraft 1.21.1
- NeoForge 21.1.227+
- Sable 1.1.3+ (required dependency — the add-on does literally nothing without it)
- Sable's bundled libraries (Sable Companion, Veil) — these ship inside Sable's jar; you do not need to install them separately.
Compatibility
- Server + client. Effects are client-side; the destruction logic itself runs server-side.
- Should be compatible with most other mods. Modded blocks are auto-classified into a safe fragile tier.
Known limitations
- Bedrock, barriers, and unbreakable blocks are skipped (by design —
BlockToughness.Entry.UNBREAKABLEand the universal toughness gates make sure they survive even inside sub-levels). - Block entities (chests, furnaces, Create cogwheels, Mekanism cables, etc.) ARE broken since 1.6.1 but only via the VANISH path — peeling them into a new sub-level is opt-in via
allowBlockEntityDetach=trueand may corrupt mod-side network state. - Detach respects Sable's parent boundaries — clusters never cross sub-levels.
Addon API (since 1.1.0)
Sable: Destructive ships a small, stable, SemVer-covered public API so other mods can teach it about their blocks. Unknown modded blocks otherwise fall back to a deliberately fragile clay-tier default — register an entry and your block participates in real impact / explosion physics with the mass and toughness you choose.
The entire API lives in a single class: com.destroynautics.sabledestructive.api.SableDestructiveAPI. Anything outside that package is internal and may change between versions.
Quick example
@Mod("examplemod")
public class ExampleMod {
public ExampleMod(IEventBus bus) {
bus.addListener(this::onCommonSetup);
}
private void onCommonSetup(FMLCommonSetupEvent e) {
// Direct Block reference (recommended, type-safe):
SableDestructiveAPI.register(
ExampleBlocks.MITHRIL_BLOCK.get(),
8500.0, // mass in kg
SableDestructiveAPI.Tier.NETHERITE);
// By ResourceLocation (works even if the target Block class isn't loaded —
// useful for optional cross-mod compat):
SableDestructiveAPI.register(
ResourceLocation.fromNamespaceAndPath("othermod", "arcane_brick"),
3000.0,
SableDestructiveAPI.Tier.STONE);
// Custom raw values (advanced):
SableDestructiveAPI.register(
ExampleBlocks.FOAM_PANEL.get(),
40.0, // very light (kg)
0.8); // very fragile (~8 toughness units)
// Bedrock-equivalent:
SableDestructiveAPI.registerUnbreakable(ExampleBlocks.WARDSTONE.get());
}
}
Tier presets
Use SableDestructiveAPI.Tier instead of magic numbers — your blocks then stay balanced relative to vanilla as the mod's defaults evolve.
| Tier | Toughness (units) | Vanilla example |
|---|---|---|
PAPER |
10 | flowers, candles |
LEAF |
15 | leaves, vines |
EARTH |
20 | dirt, sand, gravel |
CLOTH |
30 | wool, hay, cobwebs |
SOFT |
40 | sponge, slime, snow |
CLAY |
60 | clay, ice, terracotta |
WOOD |
120 | logs, planks |
STONE |
200 | stone, cobble |
HARD_STONE |
280 | deepslate |
METAL_SOFT |
340 | gold, copper |
METAL |
420 | iron, anvil |
GEM |
520 | diamond, emerald |
OBSIDIAN |
700 | obsidian |
NETHERITE |
1000 | netherite, ancient debris |
REINFORCED |
2000 | reinforced deepslate |
UNBREAKABLE |
∞ | bedrock, barrier |
Method reference
// Register / override
SableDestructiveAPI.register(Block, double massKg, double breakSpeedMs)
SableDestructiveAPI.register(Block, double massKg, Tier tier)
SableDestructiveAPI.registerUnbreakable(Block)
// By ResourceLocation (optional cross-mod compat)
SableDestructiveAPI.register(ResourceLocation, double massKg, double breakSpeedMs)
SableDestructiveAPI.register(ResourceLocation, double massKg, Tier tier)
SableDestructiveAPI.registerUnbreakable(ResourceLocation)
// Inspect / remove
boolean SableDestructiveAPI.isRegistered(Block)
void SableDestructiveAPI.unregister(Block)
// Read-only queries (e.g. for your own tooltips / ballistics)
double SableDestructiveAPI.getMassKg(BlockState)
double SableDestructiveAPI.getBreakSpeedMs(BlockState)
double SableDestructiveAPI.getToughnessUnits(BlockState) // m/s × 10
Notes
- Thread-safe. Call from any thread; safe during mod loading and at runtime.
- Last write wins. Re-registering the same block replaces the previous entry.
- Idempotent. Multiple calls with the same arguments are harmless.
- Mass is in kilograms; the block is a 1m³ cube. Mass affects explosion kick scaling and diagnostics, not the break decision itself — that one is purely break-speed (m/s).
- F3+H tooltip displays
Toughness: N unitswhereN = breakSpeedMs × 10.
Soft dependency setup
Add Sable: Destructive as an optional runtime dependency in your neoforge.mods.toml so your mod loads either way:
[[dependencies.examplemod]]
modId = "sabledestructive"
type = "optional"
versionRange = "[1.1.0,)"
ordering = "AFTER"
side = "BOTH"
Then guard your registration call so it doesn't NoClassDefFoundError when Sable: Destructive isn't installed:
if (ModList.get().isLoaded("sabledestructive")) {
ExampleSableCompat.register(); // wraps the SableDestructiveAPI calls
}
Credits
- Author / code: Xylos_Official
- Visuals: Viaquelt
- Built on top of: Sable by ryanhcode


