promotional bannermobile promotional banner

Runic Skills

Runic Skills is a Minecraft 1.20.1 Forge RPG progression mod that adds ten levelable skills, auto-scaling passive bonuses, and hundreds of toggleable perks so players can shape a real build through normal gameplay. Designed for the Runecraft modpack.

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

Forge

implementation fg.deobf("curse.maven:runic-skills-1516283:7976040")
Curse Maven does not yet support mods that have disabled 3rd party sharing

Learn more about Curse Maven

[0.9.9] - 2026-04-24

Added

  • maxActivePerks config (HandlerCommonConfig, group general, default 0 = unlimited, range 0–256). Caps the number of perks a player can have enabled at once. Enforced server-side in TogglePerkSP on the rank 0 → ≥1 transition (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 to ironsMaxSchoolSelections. Mirrored through CommonConfigSyncCP.
  • disabledPerks / disabledPassives kill-switch lists (HandlerCommonConfig, @ListGroup of String). 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() returns false for disabled perks, so every event handler short-circuits automatically. Passives: RegistryAttributes.modifierAttributes passes enabled=false to amplifyAttribute, which removes the modifier — single choke point means all attribute effects drop to zero. /skillsreload now re-runs modifierAttributes for every connected player so passive-disable changes take effect without relog. Registry path ("berserker") and full-id ("runicskills:berserker") forms are both accepted. Mirrored through CommonConfigSyncCP.
  • perkSwapCooldownTicks config (HandlerCommonConfig, default 0 = no cooldown, range 0–72000). Per-player cooldown between perk enables. Piggybacks on the existing perkCooldowns map via new SkillCapability.COOLDOWN_PERK_SWAP constant, which already ticks down every server tick via TickEventHandler. 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.
  • skillLevelUpCostMultiplier config (HandlerCommonConfig, default 1.0, range 0.1–10.0). Scales the vanilla XP cost of leveling a skill. Applied in both SkillLevelUpSP.requiredPoints (XP-points cost) and requiredExperienceLevels (level-gate) so high-level players can't bypass an increased cost. Mirrored through CommonConfigSyncCP so 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 Forge SimpleJsonResourceReloadListener, subscribed via AddReloadListenerEvent), PerkGroupsSyncCP (new PLAY_TO_CLIENT packet, sent on login and /skillsreload). Enforced in TogglePerkSP alongside 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_VERSION from 3 to 4. Wire format of CommonConfigSyncCP has grown (six new fields: maxActivePerks, disabledPerks, disabledPassives, perkSwapCooldownTicks, skillLevelUpCostMultiplier; plus the new PerkGroupsSyncCP packet). 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.
  • /skillsreload now also broadcasts PerkGroupsSyncCP to every connected player so datapack perk-group changes propagate without relog.
  • Perk.isEnabled(Player) and Perk.isEnabled() now consult RegistryPerks.isDisabled(perk) before returning true, covering every perk-effect event handler in a single choke point.
  • RegistryAttributes.modifierAttributes now checks RegistryPassives.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$ClientProxy is a @Mod.EventBusSubscriber class, so Forge's AutomaticEventSubscriber.inject loads it at mod construction via Class.forName(..., true, loader). The clientSetup method contained an inline lambda () -> TabsMenu.register(new LegendaryTabRunicSkills()), which the Java compiler desugared into a synthetic lambda$clientSetup$3 method on ClientProxy itself. The JVM verifier walks that method body at class-load time, finds the INVOKESTATIC sfiomn/.../TabsMenu.register(TabBase) + NEW com/otectus/.../LegendaryTabRunicSkills pair, and has to check that LegendaryTabRunicSkills is assignable to TabBase. That assignability check eager-resolves TabBase, which fails with NoClassDefFoundError when Legendary Tabs is absent — even though the if (LegendaryTabsIntegration.isModLoaded()) guard means the lambda would never actually run. Fixed by replacing the inline lambda with a method reference LegendaryTabsClientIntegration::registerTab that delegates to a new registerTab() static method on the existing client-integration class. ClientProxy's bytecode now only references LegendaryTabsClientIntegration (a plain class not in the optional-mod namespace); the sfiomn.* types stay confined to LegendaryTabsClientIntegration and LegendaryTabRunicSkills, which are only class-loaded when the isModLoaded() guard passes. The inline sfiomn.legendarytabs.api.tabs_menu.TabsMenu.register(...) call and the now-unused import com.otectus.runicskills.client.gui.LegendaryTabRunicSkills were removed from RunicSkillsClient.java.
  • Skill-selection hover border misaligned. skill_card_hover.png is 74×26 (a symmetric green halo with 4px of glow on each horizontal side of the underlying 66×26 button), but RunicSkillsScreen.drawOverview was passing OVERVIEW_SLOT_WIDTH (66) as both the blit size and the textureWidth argument to GuiGraphics.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 introducing OVERVIEW_HOVER_TEX_WIDTH/HEIGHT constants (74×26), passing them as the real texture dimensions, and shifting the blit position by (74-66)/2 = 4px left 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. drawOverview was calling Utils.drawToolTip inside 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 /reload updates perk-group state server-side but does not auto-push to clients (matches existing lock-items behavior). Use /skillsreload for 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.