File Details
Ars 'n Spells 1.9.0 (Forge 1.20.1)
- R
- May 11, 2026
- 252.86 KB
- 780
- 1.20.1
- Forge
File Name
ars_n_spells-1.9.0.jar
Supported Versions
- 1.20.1
Curse Maven Snippet
[1.9.0] - 2026-05-10
Bug Fixes (P0 stabilization pass)
- AffinitySyncPacket no longer crashes dedicated servers. Pre-1.9.0, AffinitySyncPacket imported
net.minecraft.client.Minecraftdirectly and was registered unconditionally on the common bus, so the very first cast on a dedicated server attempted to loadMinecraftserver-side and risked aNoClassDefFoundError. Client logic moved to a newClientAffinityPacketHandlerand the packet now wraps the client-side capability mutation inDistExecutor.unsafeRunWhenOn(Dist.CLIENT, …)— the same patternResonanceSyncPacketwas already using. - Iron's Spellbooks is now actually optional.
IronsLPHandlerwas an unconditional@Mod.EventBusSubscriberwhile importingio.redspace.ironsspellbooks.api.*at the file header — Iron's-less servers crashed at classload. The annotation is removed and the handler is now instance-registered behind the existingModList.get().isLoaded("irons_spellbooks")block inArsNSpells.SpellScalingUtil's static initializer (which referenced Iron'sAttributeRegistry.*_SPELL_POWERslots) is converted to lazy double-checked init so the map only builds when an Iron's-aware caller actually invokes it. NewIronsCompatexposes a cachedisLoaded()for the cast hot-path. - Scroll LP handling is now a real transaction.
MixinScrollItemused to consume LP at scroll-use time, ignore the consumption return value, and silently leak the death-on-insufficient-LP path becauseLPDeathPreventionearly-returned when the death-mode config was on. The mixin is rewritten as validate → cast → commit: HEAD callshasEnoughLP, stages a per-player pending entry via the newScrollLPTrackerhelper, and either cancels (safe mode) or lets the scroll proceed (death mode). RETURN reads the originaluseresult and either consumes LP (success), kills the player explicitly (death mode), or no-ops (cast didn't actually consume the action). LP no longer disappears for failed casts, and the death-mode path now does its own enforcement instead of relying on a subsystem that exited early. - Cooldown "namespacing" was a lie — now removed.
UnifiedCooldownManageraccepted aString modNamespaceargument that suggested per-mod isolation, but the storage was alwaysMap<CooldownCategory, Long>and the namespace only ever appeared in debug logs. The parameter is dropped from every public method, both callers (CooldownHandlerandIronsCooldownHandler) updated, and the README "Cooldowns" section now states clearly that the unified system is global per category — an ArsOFFENSIVEcast and an Iron'sOFFENSIVEcast intentionally collide. NBT and packet wire formats are unchanged, so existing 1.8.9 saves load cleanly with no migration.
Finished half-wired systems
The pre-1.9.0 README claimed cross-mod progression "and vice versa", "Ars spell potency scales with Iron's spell power attributes", and optional affinity decay. None of those were actually wired. They are now:
- Iron's-side progression hook. New
IronsProgressionHandlerlistens to Iron'sSpellOnCastEvent, derives the school from the path component of the school's resource location, and calls into the sameProgressionData.incrementCastCount+<school>_spell_powerattribute application that the Ars-side handler uses. The shared logic lives inProgressionAttributesso both sides use the same modifier UUID and naming. - Iron's-side affinity hook. New
IronsAffinityHandlermirrorsAffinityHandlerfor Iron's casts. TheAffinityTypeenum gainsHOLY,ENDER,BLOOD,EVOCATION,ELDRITCHso every Iron's stock school maps onto an entry. Adding enum values is forward and backward compatible — pre-1.9.0 NBT loads cleanly with the new entries defaulting to 0. - Ars spell scaling actually wired. New
ArsSpellScalingHandlercomputesSpellScalingUtil.getMultiplierForCasteron each ArsSpellCastEvent, stages it for the casting player with a 60-tick window, and applies it onLivingHurtEventfor spell-flavored damage from that player. Final amount is clamped againstspell_power_cap. Filter rejects melee/environmental damage so the bonus only flows to actual spell hits. - Affinity decay tick handler. New
AffinityDecayHandlerticks each player everyaffinity_decay_interval_ticks(default 1200 = 60 s) and prorates the existingaffinity_decay_ratefrom per-day to per-interval (24000 ticks per Minecraft day). Default forenable_affinity_decayis nowfalsefor new configs to avoid surprising existing players whose 1.8.9 config had it (no-op-ly) on; existing config files retain their previous setting. - Login affinity sync. New
AffinitySyncOnLoginHandlerfires oneAffinitySyncPacketper non-zero school when the player joins, so HUD and tooltips reflect persisted state immediately instead of waiting for the next cast. Progression already auto-applies attribute modifiers on login so it doesn't need a packet sweep. AffinityCalculatorandAffinityBonuses— the per-level damage curve now lives inAffinityCalculator.getDamageBonusand is consumed byAffinityBonuses.getAttributeMultiplier. Same numbers as before; less duplication; no more dead code.
Configuration
- New:
affinity_decay_interval_ticks(default 1200, range 20–24000) — how often the new decay handler ticks each player. - Changed default:
enable_affinity_decayis nowfalsefor fresh configs (was effectively a no-optruein 1.8.9). Existing configs preserve whatever value the user already had.
Internal
- Mixin-package isolation respected. The transactional scroll-cost state (
ScrollLPTracker) lives in thecompatpackage, not as an inner class ofMixinScrollItem. Sponge Mixin treats every class inside a mixin package — including a mixin's own inner classes — as off-limits for direct reference, because the mixin class gets merged into its target at load time and stops existing as a standalone class. The first 1.9.0 build paid for that rule with anIllegalClassLoadErroronMixinScrollItem$PendingScrollLP; extracting the holder fixed it without behavior change. Worth remembering for any future mixin that needs shared state.
Known follow-ups (deferred to 1.9.1+)
- Aura HUD does not auto-update server-side regen — still updates only on next spell cast or login. Aura sync is queued for 1.10.0.
SERVER/CLIENTconfig split. All keys remain on the singleCOMMONconfig in 1.9.0.- Datapack registries for spell schools, cooldown categories, progression rules, cross-cast rules.
- Cross-cast NBT re-validation at cast time (server-trust hardening).
- Capability sync on dimension change / respawn (only login is in scope this round).
Backward compatibility
Strict. AffinityData, ProgressionData, CooldownData, AuraCapability NBT shapes unchanged. Network protocol stays at "1". Inscribed cross-cast items unaffected. No removed config keys, no renamed config keys.

