File Details
Runic Skills 0.9.7 (Forge 1.20.1)
- R
- Apr 23, 2026
- 1.13 MB
- 75
- 1.20.1
- Forge
File Name
runicskills-0.9.7.jar
Supported Versions
- 1.20.1
Curse Maven Snippet
[0.9.7] - 2026-04-22
Added
enableItemLocksmaster toggle inHandlerCommonConfig(defaulttrue). When off, every entry inrunicskills.lockItems.json5and every integration-generated lock is ignored —SkillCapability.canUse(...)short-circuits totrue. This is the single switch users were looking for when they "disabled itemlock" expecting items like the trident to become usable; previously, the only "lock" toggles in the YACL UI weredropLockedItems(only controls auto-dropping, not the lock check) and the per-integration*EnableLockItemsflags (only gate integration-generated entries, not the vanilla list that contains the trident). The new flag is mirrored intoClientCapabilityAccess.canUseItemClientso client-side checks (used byMixGunItemand gun-mod integrations) honour it too.ConfigSyncCP.sendToAllPlayers()— broadcasts the lock-items list to every connected client. Wired into/skillsreloadand/registeritemso cache changes propagate without requiring every player to relog.
Changed
dropLockedItemsconfig comment now notes that it has no effect whenenableItemLocksis off.- Bumped network channel
PROTOCOL_VERSIONfrom2to3.CommonConfigSyncCPnow carries theenableItemLocksboolean ahead ofdropLockedItems; the wire format is incompatible with the previous version. /skillsreloadnow also re-sendsCommonConfigSyncCPandDynamicConfigSyncCPto every connected player so config edits made between joins are picked up without requiring relogs.
Fixed
- Trident (and every other vanilla locked item) appeared "stuck" locked even after the user toggled lock-related options off. Root cause: there was no master toggle.
dropLockedItemsand the*EnableLockItemsper-integration flags do not gate the vanillalockItemListentries (which includeminecraft:trident#strength:20;dexterity:18), so anyone trying to "disable itemlock" had no effect on vanilla items. The newenableItemLockstoggle gives a single switch. /registeritem <skill> <level>did not refresh the cache when adding a skill to an existing locked-item entry. The "remove" and "add new item" branches calledHandlerSkill.ForceRefresh(); the "add skill to existing item" branch only saved the file to disk, so the new requirement was silently ignored until the next reload. Now refreshes on all three branches.- Lock-items cache was server-only after
/skillsreloadand/registeritem. Connected clients kept their staleSkillsmap, and sinceInteractionEventHandler(which callscanUseItem) runs on both sides, a client with the stale cache would cancel right-clicks before the server even saw them. Both commands now broadcastConfigSyncCPto every player.
[0.9.6] - 2026-04-22
Added
LegendaryTabsClientIntegration.synchronizeTabStripAcrossScreens— runs once atFMLLoadCompleteEvent(i.e. after every mod'sTabsMenu.registercall has drained) and reflectively does a two-way sync against Legendary Tabs' privatetabsScreensmap: - Forward: every tab registered onInventoryScreenis re-registered onRunicSkillsScreenwith its original priority preserved, so the Skills page shows the same full tab strip a player sees on the vanilla inventory (same tabs, same order, same X anchor). - Reverse: the Skills tab is also registered on every other screen that already hosts Legendary Tabs' built-inInventoryTab(FirstAid Medkit, Curios, Travelers Backpack, Reskillable/Pufferfish skill pages, etc.), so it stays visible when the player switches between inventory-companion screens. - The whole pass is wrapped in try/catch againstNoSuchFieldException/IllegalAccessException/ClassCastException; any failure logs a single warning and the mod falls back to the prior "Skills tab only on the Skills screen" behaviour.libs/l2tabs-0.3.3.jar— compile-time API stub (3.4 KB). Contains only the four type signatures (BaseTab,TabToken,TabManager,TabRegistrywith itsTabFactoryfunctional interface) thatTabRunicSkillsandRunicSkillsClientreference.compileOnlykeeps it out of the shipped jar; when the real L2Tabs 0.3.3 mod is installed, its classes shadow the stub at runtime. Lets the project build when the upstream L2Tabs jar isn't locally available, without any build-script changes. Ships with aL2TABS_STUB_README.txtdocumenting the arrangement.
Changed
- Skills tab now renders from Legendary Tabs' own
tab_menu_buttons.pngatlas at(u=27, v=92)— the plain silver sword tile that no built-in tab class claims. Hover state uses the+54 Ushift that every built-in tab uses. This removes therenderItem(leveling_book, …)overlay, the customlegendary_tab.pngtexture, and makes the Skills tab byte-for-byte identical in frame shape, shading, palette, and hover transition to every neighbouring tab — including the exact highlight bevel rows that hand-drawn reproductions were missing (which previously read as "1 pixel too tall" because the flat top edge lacked the real tab's 2-row white highlight strip). LegendaryTabsClientIntegration(new) — split out ofLegendaryTabsIntegrationso the shared class stays dedicated-server-safe (nonet.minecraft.client.*imports — silences the:checkSidedImportslint task). The client file lives underclient/integration/, fully inside the allowlist.- Default
legendaryTabsPrioritylowered from80to15. Bytecode audit of Legendary Tabs 1.1.3.1 confirmsScreenInfo.tabsis aTreeMap<Integer, List<TabBase>>(ascending order = render order) and the built-in tab priorities areInventoryTab=10,Backpacked/TravelersBackpack=20,Reskillable*=30,PassiveSkillTree/PufferfishsSkills=40,BodyDamage=50,Diet=60,FtbQuests=70,Maps (JourneyMap/MapAtlases/Xaeros)=75,FtbTeams=80. Priority 15 sits strictly between Inventory and everything else, so Skills renders as the second tab in the strip regardless of which integrations are loaded. Config comment updated with the full priority table.
Fixed
- Skills tab rendered as a floating book icon instead of a proper tab inside Legendary Tabs. Root cause:
LegendaryTabRunicSkills.renderdrew only the item viagfx.renderItem(…)with no background. Legendary Tabs'TabButton.renderWidgetdoes not invokesuper, so tabs are fully responsible for drawing their own 26×22 frame; every built-in tab does so by blitting from the shared atlas. Fixed by switching to an atlas blit (see Changed). - Opening the Skills screen collapsed Legendary Tabs' strip to just the Skills icon.
TabsMenu.initScreenButtonskeys offscreen.getClass()(exact match, notinstanceof) and iterates only tabs explicitly registered for that class. Fixed by the forward half ofsynchronizeTabStripAcrossScreens. - Skills tab disappeared when any other inventory-companion screen (Medkit, Curios, Backpack, …) was open. Same root cause as above but in the other direction — our tab wasn't registered on those screens' classes. Fixed by the reverse half of
synchronizeTabStripAcrossScreens. - Build failed with "Could not find dev.xkmc.l2tabs:l2tabs:0.3.3" cascading into 20+ follow-on resolution errors. Single root cause:
libs/l2tabs-0.3.3.jarwas missing and no public Maven repository hosts the coordinate. When:__obfuscatedconfiguration failed on that one dep, ForgeGradle's deobf pipeline aborted without populatingbundled_deobf_repofor any of the otherfg.deobf(...)entries. Fixed by shipping the compile-time stub (see Added).
Removed
assets/runicskills/textures/gui/legendary_tab.png— custom hand-drawn frame texture. No longer referenced now that the Skills tab blits directly from Legendary Tabs' shared atlas.
[0.9.5] - 2026-04-22
Added
- Legendary Tabs (Sfiomn) integration. When
legendarytabsis present, Runic Skills registers a nativeTabsMenutab (LegendaryTabRunicSkills) duringFMLClientSetupEventso the Skills tab participates in Legendary Tabs' own UI instead of being drawn twice. LegendaryTabsIntegrationcompat layer (detects the mod viaModListand gates the native-tab registration).build.gradlenow pullssfiomn.legendarytabs:legendarytabs:1.20.1-1.1.3.1as acompileOnlydependency, resolved from the locallibs/directory (jar is not redistributed — drop it in yourself to build).legendaryTabsPriorityconfig inHandlerConfigClient— defaults to500; controls ordering within Legendary Tabs' strip (lower = earlier).- Three new lang keys (
tooltip.perk.rank,tooltip.perk.next_rank,tooltip.edit_title) translated across all 17 supplied languages; they replace hard-coded English strings in the perk tooltip and the title-edit button. HandlerConditionsnow registersEntityKilledByas the canonical title-condition name; the typoedEntiyKilledByremains registered as a deprecated alias so existing title configs continue to work.
Changed
MixInventoryScreennow bails out of its render and mouse-click injects when Legendary Tabs is loaded, deferring to the native tab registration. Prevents double-rendering of the Runic Skills tab inside Legendary Tabs' wrapped screens. The two guard branches are consolidated into arunicskills$externalTabsActive()helper andgetRecipeBookComponent().isVisible()is now called once per frame (also null-guarded) instead of twice.KubeJSIntegration.postLevelUpEventcaches its reflectiveClass/Method/field lookups on first successful resolve instead of redoing six reflective calls every level-up.Utils.FONT_COLORis nowfinal; addedSKILL_ABBR_COLOR,SKILL_LEVEL_COLOR,TITLE_SELECTED_COLOR,TITLE_UNSELECTED_COLORconstants and switched the corresponding hard-coded values inRunicSkillsScreen.MixVillagerhaggler-delta map now drops entries that no longer correspond to offers on the villager, preventing a minor memory leak when a trade is completed between UI opens.
Fixed
- Legendary Tabs integration — three distinct integration defects discovered via jar-disassembly audit:
- (A) Duplicate tab strip rendered on the Skills screen.
RunicSkillsScreen.render()calledDrawTabs.render/mouseClicked/onCloseunconditionally, and theMixInventoryScreenbail-out only covered the vanillaInventoryScreen. Now gated behind!L2TabsIntegration.isModLoaded() && !LegendaryTabsIntegration.isModLoaded()at all three call sites. - (B) Skills tab invisible on the vanilla inventory strip. DefaultlegendaryTabsPrioritywas500; disassembly ofsfiomn.legendarytabs.client.tabs_menu.InventoryTab(priority10) andFtbQuestsTab(priority70) revealed that Legendary Tabs uses small priority integers and paginates overflow. At 500 our tab always landed on a later page. Default dropped to80so the Skills tab sits right after built-in tabs on page 1. Config comment updated to explain the priority convention for pack authors. - (C) Legendary Tabs strip hidden on the Skills screen.LegendaryTabRunicSkillsregisteredRunicSkillsScreen.classwith the vanilla-inventoryVANILLA_GUI_HEIGHT = 166, but the Skills panel is actually 194 pixels tall (PANEL_HEIGHT).TabsMenu.initScreenButtonscomputedtopScreenPos = (screenHeight - 166) / 2and drew the strip 14 pixels too low — underneath our panel background. Now registersRunicSkillsScreenwithRUNIC_SKILLS_GUI_HEIGHT = 194. - Singleplayer world won't load — kicks player back to the multiplayer screen:
MixPlayer.runicskills$modifyMaxAirfires duringEntity.<init>(specifically thesetAirSupply(getMaxAirSupply())call in the constructor), which runs beforeLivingEntity.defineSynchedDataregistersDATA_HEALTH_ID. The perk check inside (Perk.isEnabled(player) → player.isDeadOrDying() → player.getHealth() → SynchedEntityData.get(DATA_HEALTH_ID)) NPE'd because the accessor hadn't been registered yet. Server thread threw "Couldn't place player in world / Invalid player data", disconnected the integrated-server client, and the UI flow bounced to the last-seen join screen. Fixed in two places: (1)Perk.isEnabled(Player)now usesplayer.isRemoved()(a simple field read) instead ofplayer.isDeadOrDying()to gate dead players; (2)MixPlayer.getMaxAirSupplybails early whenSkillCapability.get(player)returnsnull, which is always the case duringEntity.<init>(capabilities are attached by Forge after the constructor returns), guaranteeing we never invokePerk.isEnabledinside the constructor path. - Crash on attack-range modifier (Better Combat / AttackRangeExtensions):
MixTargetFinder.apply$AttackRangeModifiershad a switch statement with nobreak;betweencase ADDandcase MULTIPLY, causing every additive modifier to also be applied multiplicatively (and vice versa). Rewritten as an arrow-form switch expression. - Crash when a skill-gated gun is fired (PointBlank):
MixGunItem.tryFirecalledci.cancel()on aCallbackInfoReturnable<Boolean>with no return value set, which is illegal under Mixin and threwIllegalStateException. Now callsci.setReturnValue(false). - Crash on empty title queue:
TitleQueue.peek()anddequeue()no longer throwNoSuchElementExceptionwhen the queue is empty;peek()returnsnullanddequeue()is a no-op. - NPE in
OverlaySkillGui: whenHandlerSkill.getValue(skill)returnednull(unknown skill key),showWarningstill armedshowTicks > 0, causing the render loop to NPE onskills.size(). Now bails early without setting the ticker. - Multiple NPE paths when the local skill capability is not yet synced:
RunicSkillsScreen.drawTitleButton,handleOverviewClick, the title list renderer,buildDetailPageState, andRegistryClientEvents.onTooltipDisplaynow all null-guardSkillCapability.getLocal()and fall back to sensible empty-state rendering (requirement colour defaults to red, detail page returns null, title button shows blank). DrawTabs.renderTabVisualmatrix-stack leak: nestedpushPose/popPosepairs are now wrapped intry/finally, so a throwingrenderItem(e.g. a buggy third-party item renderer) no longer leaks two pose frames into subsequent GUI rendering.TitleCommandNPE on unset:setTitle(..., false)now null-guardsSkillCapability.get(player)before callingsetUnlockTitle.SkillCondition.ProcessVariableNPE: returns a processed value of0and bails cleanly when the player's skill capability isn't attached yet.
Removed
ScreenTabEventsdead-placeholder class. TheLegendaryTabsIntegrationjavadoc previously pointed to it as the active renderer for Legendary Tabs — in reality the nativeTabBaseis used. Javadoc rewritten to describe the real mechanism.

