File Details
Sophisticated Tab 0.3.0 (Forge 1.20.1)
- R
- May 11, 2026
- 17.39 KB
- 48
- 1.20.1
- Forge
File Name
sophisticatedtab-0.3.0.jar
Supported Versions
- 1.20.1
Curse Maven Snippet
[1.20.1-0.3.0] - 2026-05-11
Fixed
- Backpack tab chrome no longer bleeds the LegendaryTabs backpack silhouette behind the real
ItemStackicon. v0.2.0 used the LegendaryTabs atlas backpack sprite at(27, 46)as the chrome background, then overlaid the player's actual backpack item on top. The two icons visibly overlapped — the LT atlas backpack outline was visible around the edges of each player item, producing a "brown halo" on every tab. v0.3.0 ships its own backpack_tab.png atassets/sophisticatedtab/textures/gui/— a 52×22 atlas (normal atU=0, hover/active atU=26) derived pixel-for-pixel from LegendaryTabs' own Inventory tab with the central 16×16 icon rectangle overwritten with the chrome's body-fill color. The result has the exact LT tab shape (rounded top corners, dark border, top/left bevel highlight, right/bottom shadow, flat bottom) but no baked-in icon — so the realItemStackoverlays cleanly and the tab visually matches the rest of the LT strip. - Active backpack tab now stays highlighted while its
BackpackScreenis open. v0.2.0'sBackpackTab.isCurrentlyUsed(Screen)returned a flatfalse, so the tab corresponding to the currently-open backpack rendered identically to inactive tabs — unlikeBackToInventoryTab, which highlights when the inventory screen is open. v0.3.0 implements the highlight by readingBackpackScreen.getMenu()→BackpackContainer.getBackpackContext()→IBackpackWrapper.getContentsUuid()and comparing against this tab's descriptor's UUID. Because each Sophisticated Backpack stores its own UUID in NBT (IStorageWrapper.getContentsUuid()), the match survives slot moves, identical-tier backpacks, and dye/rename changes. WhenisCurrentlyUsedreturns true, LegendaryTabs sets theTabButton'sisDisabled=true, which both renders the tab in its hover state and no-ops the click (LegendaryTabs'TabButton.onPressalready early-returns when disabled).
Added
- Custom chrome asset at backpack_tab.png (52×22). Generated by .tools/generate_tab_chrome.py — checked in so future contributors can tweak colors/dimensions and regenerate via
python .tools/generate_tab_chrome.py.
Changed
- BackpackTab.java —
TEXTUREnow points at our ownsophisticatedtab:textures/gui/backpack_tab.png(52×22) instead oflegendarytabs:textures/gui/tab_menu_buttons.png(256×256).HOVER_DXreduced from 54 to 26. The blit uses the 9-argGuiGraphics.blit(rl, x, y, u, v, w, h, textureWidth, textureHeight)overload so the explicit texture-sheet dimensions are passed (the 6-arg overload would assume 256×256 and miscompute UVs). - BackpackTab.isCurrentlyUsed — implemented; see Fixed.
Design notes for future contributors
- Why a UUID instead of
(handlerName, identifier, slot)for the active-tab match?BackpackContext$ItemkeepshandlerNameandidentifierasprotectedfields with no public accessors. Reaching them from this addon's package would require either reflection or an access transformer — both fragile across upstream patch updates.IStorageWrapper.getContentsUuid()is a public interface method and gives a stable identity that survives the stack moving between slots (which would invalidate slot-based matching). - What if the player opens a
BackpackContext$ItemSubBackpack(nested backpack)? That context's wrapper has its own UUID, distinct from any top-level descriptor's UUID, soisCurrentlyUsedcorrectly returns false for every top-level tab. Sub-backpack tabs aren't enumerated yet (still on the Planned list). - Why ship the chrome PNG instead of drawing the chrome programmatically? Resource-pack authors should be able to restyle the chrome alongside other GUI textures, and a tuneable PNG asset gives the tightest control over how the button reads at GUI scales 1–4. The PNG is regenerated at build time by .tools/generate_tab_chrome.py: copy LT's Inventory tab verbatim, overwrite the central 16×16 rectangle (where the ItemStack will sit) with the chrome's body-fill color. If LT updates their tab atlas styling in a future release, run
python .tools/generate_tab_chrome.pyto refresh. - Why no
BackpackTab.isCurrentlyUsedshort-circuit when no player is online?TabBase.isCurrentlyUsed(Screen)is only invoked by LT'sTabButton.setTabBase(...), which is called fromTabsMenu.initScreenButtons(...)— a screen-init event. On a client, screens only init when a player is present. Defensive null-checks are kept anyway because LT's API doesn't guarantee the call site won't broaden.
[1.20.1-0.2.0] - 2026-05-11
Changed — One tab per backpack
Per-backpack tabs replace the single "open first backpack" tab. The previous SophisticatedBackpacksTab.java registered a single
TabBasethat calledBackpackOpenMessage()with no arguments — server-side, that opened whichever backpackPlayerInventoryProvider.runOnBackpacks(...)yielded first. v0.2.0 pre-registers eight indexed BackpackTab.java instances atLegendaryTabsCompat.register(); each instance resolves its own BackpackDescriptor.java (handlerName + identifier + slot + iconStack + wrapper) from a freshfindAllBackpacks(player)call at render time. Disabled tabs collapse out of LT'senabledTabsrow, so users with 0 backpacks see no tabs, users with 3 see 3, users with 10+ see 8 (the pool cap).Click sends the slot-aware
BackpackOpenMessage(int slot, String identifier, String handlerName). Sophisticated Backpacks 1.20.1-3.24.x already ships this constructor; verified by decompilingsophisticatedbackpacks-1.20.1-3.24.38.1738.jar. The server handler atBackpackOpenMessage.handleMessage(...)checkshandlerName.isEmpty()— if non-empty, it builds aBackpackContext$Item(handlerName, identifier, slotIndex, wasOpenFromInventory)and opens that exact backpack (with built-in slot fixup for offhand 38→2 and chest armor 40→0). No custom packet, no server-side code, no validation layer needed in this addon.Real
ItemStackicons viaGuiGraphics.renderItem. Each tab blits LegendaryTabs' backpack-shaped chrome at(27, 46)as the button background, then draws the descriptor's liveItemStackat(x+5, y+3)on top. Dye colors, anvil renames, upgrade decorations, resource-pack model overrides, and any custom item renderer flow through automatically because rendering uses the vanilla item-rendering pipeline. The tooltip isiconStack.getHoverName()— so renamed backpacks show their custom name on hover.
Added
- BackpackDescriptor.java — immutable record carrying
(handlerName, identifier, slot, iconStack, tooltip, wrapper). The wrapper is resolved once at construction so screen-sizing math doesn't have to re-walk the capability chain. - BackpackTab.java —
TabBaseconstructed with anindex0..7;isEnabled(player)returns true only whenfindAllBackpacks(player).size() > index. Priority is20 + index, putting backpack tabs immediately afterBackToInventoryTab(priority 10).
Changed
- SophisticatedBackpacksLocator.java —
findAllBackpacks(Player)now walks the entirePlayerInventoryProvider.runOnBackpacks(...)iteration (callback returnsfalseto keep going);findFirstBackpackandhasOpenableBackpacknow wrap that list rather than short-circuiting on the first hit. - SophisticatedBackpacksSizing.java —
getWidth/getHeightmap throughBackpackDescriptor::wrapperto reach theIBackpackWrapper(signature change from the locator). - LegendaryTabsCompat.java — registers
BackToInventoryTab+ 8 ×BackpackTab(i)in a loop. Pool size isBACKPACK_TAB_POOL = 8. - en_us.json — removed unused
tooltip.sophisticatedtab.tab.sophisticatedbackpacks; addedtooltip.sophisticatedtab.tab.backpack_empty("No backpack") as the fallback tooltip when a tab's descriptor is briefly unresolvable.
Removed
SophisticatedBackpacksTab.java— replaced wholesale byBackpackTab.tooltip.sophisticatedtab.tab.sophisticatedbackpackslang key — each tab now usesiconStack.getHoverName()for its tooltip.