File Details
Runic Skills 0.9.9 (Forge 1.20.1)
- R
- Apr 24, 2026
- 1.15 MB
- 15
- 1.20.1
- Forge
File Name
runicskills-0.9.9.jar
Supported Versions
- 1.20.1
Curse Maven Snippet
[0.9.9] - 2026-04-24
Added
maxActivePerksconfig (HandlerCommonConfig, groupgeneral, default0= unlimited, range0–256). Caps the number of perks a player can have enabled at once. Enforced server-side inTogglePerkSPon therank 0 → ≥1transition (rank-ups on already-active perks bypass the cap, matching existing school-attunement semantics). Iron's Spells school attunements count against this cap in addition toironsMaxSchoolSelections. Mirrored throughCommonConfigSyncCP.disabledPerks/disabledPassiveskill-switch lists (HandlerCommonConfig,@ListGroupofString). Disabled entries cannot be enabled / leveled-up and their effects are suppressed; rank and level data are preserved in NBT so re-enabling restores state. Perks:Perk.isEnabled()returnsfalsefor disabled perks, so every event handler short-circuits automatically. Passives:RegistryAttributes.modifierAttributespassesenabled=falsetoamplifyAttribute, which removes the modifier — single choke point means all attribute effects drop to zero./skillsreloadnow re-runsmodifierAttributesfor every connected player so passive-disable changes take effect without relog. Registry path ("berserker") and full-id ("runicskills:berserker") forms are both accepted. Mirrored throughCommonConfigSyncCP.perkSwapCooldownTicksconfig (HandlerCommonConfig, default0= no cooldown, range0–72000). Per-player cooldown between perk enables. Piggybacks on the existingperkCooldownsmap via newSkillCapability.COOLDOWN_PERK_SWAPconstant, which already ticks down every server tick viaTickEventHandler. Applies only on rank-0 → rank-≥1 transitions; rank-ups and disables bypass. Persists in save NBT so logging out during cooldown preserves remaining time.skillLevelUpCostMultiplierconfig (HandlerCommonConfig, default1.0, range0.1–10.0). Scales the vanilla XP cost of leveling a skill. Applied in bothSkillLevelUpSP.requiredPoints(XP-points cost) andrequiredExperienceLevels(level-gate) so high-level players can't bypass an increased cost. Mirrored throughCommonConfigSyncCPso the GUI cost display stays synced with the server.- Data-driven perk groups — new datapack loader at
data/<namespace>/perk_groups/*.json. Schema:{ "max_active": int, "perks": [string], "message": string? }. New classes:PerkGroup(record),PerkGroupManager(static volatile map,firstBlockingGroup(capability, perkName)helper),PerkGroupsReloadListener(extends ForgeSimpleJsonResourceReloadListener, subscribed viaAddReloadListenerEvent),PerkGroupsSyncCP(new PLAY_TO_CLIENT packet, sent on login and/skillsreload). Enforced inTogglePerkSPalongside the existing hardcoded school-attunement check — both systems run independently. No default groups are shipped; opt-in for pack makers. Lenient JSON parsing tolerates trailing commas / comments. Per-file parse errors are logged and skipped without blocking the rest of the load.
Changed
- Bumped network channel
PROTOCOL_VERSIONfrom3to4. Wire format ofCommonConfigSyncCPhas grown (six new fields:maxActivePerks,disabledPerks,disabledPassives,perkSwapCooldownTicks,skillLevelUpCostMultiplier; plus the newPerkGroupsSyncCPpacket). Old clients connected to 0.9.9 servers (and vice versa) will get a clean connection refusal instead of a silent state-corruption bug. Please ensure clients and servers update together. /skillsreloadnow also broadcastsPerkGroupsSyncCPto every connected player so datapack perk-group changes propagate without relog.Perk.isEnabled(Player)andPerk.isEnabled()now consultRegistryPerks.isDisabled(perk)before returning true, covering every perk-effect event handler in a single choke point.RegistryAttributes.modifierAttributesnow checksRegistryPassives.isDisabled(passive)per-passive and removes rather than adds the attribute modifier for disabled entries.
Fixed
- Mod failed to load when Legendary Tabs was not installed.
RunicSkillsClient$ClientProxyis a@Mod.EventBusSubscriberclass, so Forge'sAutomaticEventSubscriber.injectloads it at mod construction viaClass.forName(..., true, loader). TheclientSetupmethod contained an inline lambda() -> TabsMenu.register(new LegendaryTabRunicSkills()), which the Java compiler desugared into a syntheticlambda$clientSetup$3method on ClientProxy itself. The JVM verifier walks that method body at class-load time, finds theINVOKESTATIC sfiomn/.../TabsMenu.register(TabBase)+NEW com/otectus/.../LegendaryTabRunicSkillspair, and has to check thatLegendaryTabRunicSkillsis assignable toTabBase. That assignability check eager-resolvesTabBase, which fails withNoClassDefFoundErrorwhen Legendary Tabs is absent — even though theif (LegendaryTabsIntegration.isModLoaded())guard means the lambda would never actually run. Fixed by replacing the inline lambda with a method referenceLegendaryTabsClientIntegration::registerTabthat delegates to a newregisterTab()static method on the existing client-integration class. ClientProxy's bytecode now only referencesLegendaryTabsClientIntegration(a plain class not in the optional-mod namespace); thesfiomn.*types stay confined toLegendaryTabsClientIntegrationandLegendaryTabRunicSkills, which are only class-loaded when theisModLoaded()guard passes. The inlinesfiomn.legendarytabs.api.tabs_menu.TabsMenu.register(...)call and the now-unusedimport com.otectus.runicskills.client.gui.LegendaryTabRunicSkillswere removed fromRunicSkillsClient.java. - Skill-selection hover border misaligned.
skill_card_hover.pngis 74×26 (a symmetric green halo with 4px of glow on each horizontal side of the underlying 66×26 button), butRunicSkillsScreen.drawOverviewwas passingOVERVIEW_SLOT_WIDTH(66) as both the blit size and thetextureWidthargument toGuiGraphics.blit. With a mis-declared texture width, OpenGL normalised UV against 66 while the image is really 74 pixels wide — so the entire halo got squashed horizontally into the 66-wide button rect, pulling the visible border inside the button outline instead of glowing around it. Fixed by introducingOVERVIEW_HOVER_TEX_WIDTH/HEIGHTconstants (74×26), passing them as the real texture dimensions, and shifting the blit position by(74-66)/2 = 4pxleft so the halo sits centered around the button with the expected 4px outer glow on each side. - Skill-selection tooltip could be overpainted by adjacent cells.
drawOverviewwas callingUtils.drawToolTipinside the skill-iteration loop, so any cell drawn after the hovered one (specifically the right-column neighbour) rendered on top of the tooltip. Fixed by capturing the hovered skill in a local, completing the loop, and rendering the tooltip once after every cell has painted. Defensive against future art changes even though the 74×26 halo fits entirely within the 11px inter-column gap.
Notes
- Vanilla
/reloadupdates perk-group state server-side but does not auto-push to clients (matches existing lock-items behavior). Use/skillsreloadfor full propagation. - All five new config options default to behavior-preserving values (
0, empty list,1.0); upgrading an existing world changes nothing until the admin opts in. - No NBT schema changes; save-compatible with 0.9.7.

