File Details
Runic Tome 0.1.0 (Forge 1.20.1)
- R
- Apr 15, 2026
- 75.45 KB
- 4
- 1.20.1
- Forge
File Name
runictome-0.1.0.jar
Supported Versions
- 1.20.1
Curse Maven Snippet
[0.1.0] — 2026-04-13
First public release.
Added
- Runic Tome item. Epic rarity, stacks to 1, fire-resistant. Granted automatically the first time a player joins a world (
FirstJoinHandler). - Soulbound behavior. The Runic Tome is never dropped on death — any tome in the player's inventory at death is intercepted in
LivingDropsEvent, its count is recorded in a persistedstashedTomesfield on the player's capability, and the tome is restored to the new player's inventory duringPlayerEvent.Clone. The stash survives server crashes between death and respawn because it's persisted to disk alongside the rest ofRunicTomeData. Non-death clones (dimension change, End return) are a no-op.keepInventory=trueis honored —LivingDropsEventisn't fired and the vanilla path keeps the tome intact with no double-give. - Per-player virtual book capability. Unlocked books are stored in a persistent
IRunicTomeDatacapability attached to each player; persisted across death, dimension change, and item loss. - Server → client sync. Custom packet layer (
RunicTomeNetwork,UnlockBookPacket) pushes unlock events to the client-sideClientDataCache; full state syncs on login viaCapabilityEvents.syncTo. - Vanilla-book-styled GUI.
RunicTomeScreenrenders the vanillatextures/gui/book.pngat 192×192 with paginated clickable entries. Page forward/back via vanillaPageButtonwidgets or ←/→ keys. Entries land directly on page 0; clicking one closes the tome and delegates to the adapter'sopen()method. - Automatic absorption pipeline. Multiple redundant paths ensure books are absorbed the instant they enter an inventory:
EntityItemPickupEvent— ground pickup (priorityHIGH, cancels the pickup and discards the ItemEntity).PlayerEvent.ItemCraftedEvent/ItemSmeltedEvent— crafting and smelting output.TickEvent.ServerTickEvent— per-tick inventory sweep on every online player (no interval gating).PlayerContainerEvent.Close— sweep on container close, catching quest-reward GUIs and FTB-style popups.- Immediate sweep on
PlayerLoggedInEventafter the tome is granted.
- Patchouli integration (reflective, zero compile-time dependency).
PatchouliGuideAdapterrecognises both flavors of Patchouli books:- NBT fast-path for generic
patchouli:guide_bookstacks tagged with{patchouli:book: "modid:book_id"}. - Custom-item path for books declared with
dont_generate_book: true(e.g. Ars Nouveau's Worn Notebook, Botania's Lexica Botania). Builds anItem → BookKeymap by reflectively walkingvazkii.patchouli.common.book.BookRegistry.INSTANCE.booksand calling each Book'sgetBookItem()method. - Map is pre-warmed on
OnDatapackSyncEvent(viaPatchouliReloadHandler) so the first absorption doesn't pay the build cost inline. - Client-side book opening via
IPatchouliAPI.openBookGUI(ResourceLocation). - Book display names read from
Book.name(supports both translation keys and literal strings); falls back to a title-cased book ID.
- NBT fast-path for generic
- Tinkers' Construct integration. All six standard books registered automatically when
tconstructis loaded: Materials and You, Puny Smelting, Mighty Smelting, Fantastic Foundry, Encyclopedia of Tinkering, Tinkers' Gadgetry. Uses the genericItemBasedAdapter. - Config-driven book allowlist.
extraBookItemIdsinrunictome-common.tomllets modpack authors register any standalone item as a guide book without code changes. Invalid/missing entries are logged and skipped. - Public integration API.
RunicTomeAPI.registerAdapter(GuideSystemAdapter)andGuideSystemAdapterinterface allow third-party mods to register their own guide systems. - IMC integration.
InterModComms.sendTo("runictome", "register_adapter", ...)supports mods that don't want a compile-time dependency on Runic Tome. Handled byImcHandler. - Diagnostic logging. On startup, logs
"Runic Tome: registered N guide-book adapter(s): [...]"at INFO. On first absorption of an unrecognised book-like item (name contains "book"/"manual"/"guide"/"lexicon"/"tome"), logs a once-per-item hint at INFO telling the user to add it toextraBookItemIds.
Known good with
- Patchouli 1.20.1-85-FORGE and later.
- Tinkers' Construct 1.20.1.
- Ars Nouveau (via Patchouli custom-item path — Worn Notebook).
- Ice & Fire Delight (via Patchouli NBT path — Cookbook).
- Tested in the Runecraft modpack environment against a large (100+ mod) instance.
Fixed during development
The following issues were caught and corrected before release. Captured here for posterity.
IPatchouliAPIClassNotFoundException. The interface is declared as a nested interface insidevazkii.patchouli.api.PatchouliAPI, not as a top-level class. Early iterations ofPatchouliGuideAdapter.tryInit()used the top-level name and threwClassNotFoundException, which the outer catch swallowed as "Patchouli not present" — leading to zero adapters being registered even when Patchouli was loaded. Fixed by trying the binary nested-class name (vazkii.patchouli.api.PatchouliAPI$IPatchouliAPI) first and falling back to the top-level name for forward compatibility.- Custom-item Patchouli books silently unrecognised. The original
identify()method only matched items whose registry ID was literallypatchouli:guide_book, rejecting any book declared withdont_generate_book: true. Fixed by adding the reflectiveBookRegistrywalk andItem → BookKeymap. getBookStack()returning empty. An early version ofbuildCustomItemMap()went throughIPatchouliAPI.getBookStack(id)via a cachedAPI_INSTANCE. Under certain setup orderingsPatchouliAPI.get()returns a stub that returns empty stacks, so the map built with 0 entries. Fixed by switching toBook.getBookItem()called directly on each Book object, bypassing the API layer entirely.- Per-book errors silently swallowed.
buildCustomItemMap()originally logged per-book failures at DEBUG, so a 0-entry map looked like "everything worked, zero custom books exist." Upgraded to WARN with a counts summary(mapped, total, generic, errors). - "Open" button did nothing. Early
PatchouliGuideAdapter.open()cached only the server-side 2-arg overload (openBookGUI(ServerPlayer, ResourceLocation)) and invoked it from the client with aLocalPlayer, throwingIllegalArgumentExceptioninside a try/catch. Fixed by caching both client and server overloads and preferring the client 1-arg variant when called from the screen. - Lowercase book titles.
displayName()returned the rawbookId.getPath(). Fixed by reflectively readingBook.nameand wrapping inComponent.translatable(...); falls back to a title-cased ID. - Slow startup absorption. Books granted to a player after login (e.g. via KubeJS, FTB Quests, or any path that calls
Inventory.add()directly) bypass every Forge pickup event and were only caught by the periodic sweep, which ran every 20 ticks (up to 1 second of latency). Removed theinventorySweepIntervalTicksconfig knob entirely and switched to per-tick sweeping plusPlayerContainerEvent.Closehandling. - Inventory sweep sync gap.
ServerTickHandler.scanContainermutated inventory slots directly viaslots.set(i, ItemStack.EMPTY)without notifying the container menu. Added explicitinv.setChanged()andsp.inventoryMenu.broadcastChanges()calls after any removal. - GUI title-page detour. The vanilla-book GUI originally opened on a title/count page and required a forward-arrow click to see entries. Dropped the title page; entries render from page 0.