Skip to content
LlaGuiTo edited this page Sep 28, 2026 · 1 revision

API

The API differs between versions. Differences are marked below.

The public API lives in com.skd.vellumli.api. It is designed to be dependency-free: if Vellumli is not installed, VellumliAPI.get() still returns a no-op stub, so code that calls it will not crash. Many methods only make sense on the physical client; those assert that they are called client-side.

Between 1.21.1 and 26.2, Minecraft renamed some types: ResourceLocation became Identifier, and GuiGraphics became GuiGraphicsExtractor. Where a signature is affected, both versions are shown.

Obtaining the API

Identical in both versions.

import com.skd.vellumli.api.VellumliAPI;

VellumliAPI.IVellumliAPI api = VellumliAPI.get();
api.isStub(); // true when Vellumli is absent

VellumliAPI.MOD_ID is "vellumli" and VellumliAPI.LOGGER is the mod logger.

Books and navigation

1.21.1 26.2 Side Description
openBookGUI(ServerPlayer player, ResourceLocation book) openBookGUI(ServerPlayer player, Identifier book) both Ask a player's client to open a book on its last page.
openBookEntry(ServerPlayer player, ResourceLocation book, ResourceLocation entry, int page) openBookEntry(ServerPlayer player, Identifier book, Identifier entry, int page) both Ask a player's client to open a specific entry at a zero-indexed page.
openBookGUI(ResourceLocation book) openBookGUI(Identifier book) client Open a book locally.
openBookEntry(ResourceLocation book, ResourceLocation entry, int page) openBookEntry(Identifier book, Identifier entry, int page) client Open an entry locally.
ResourceLocation getOpenBookGui() Identifier getOpenBookGui() client The book id of the currently open book, or null.
Component getSubtitle(ResourceLocation bookId) Component getSubtitle(Identifier bookId) both The edition string shown under the book title.
ItemStack getBookStack(ResourceLocation book) ItemStack getBookStack(Identifier book) both An ItemStack of the guide book pointing at the given book.
— Holder<Item> getBookItem() both The guide book item as a registry holder. 26.2 only.
— TypedDataComponent<Identifier> makeBookDataComponent(Identifier book) both A data component pointing at the given book. 26.2 only.
— @Nullable ItemStackTemplate getBookStackTemplate(Identifier book) both A book item stack template with its data component set to the given book. 26.2 only.

getBookItem, makeBookDataComponent and getBookStackTemplate do not exist on 1.21.1.

Config flags

Identical in both versions.

Config flags are boolean values used by the flag field on categories, entries, pages and components.

api.setConfigFlag("mymod:hardmode", true);
boolean hardmode = api.getConfigFlag("mymod:hardmode");

Set flags once during mod setup. See Configuration for the flags Vellumli sets itself.

Templates and text commands

registerCommand and registerFunction are identical in both versions:

// $(mymod:version) -> a fixed string
api.registerCommand("mymod:version", styleStack -> "1.2.3");

// $(mymod:config:key) -> looks up a config value
api.registerFunction("mymod:config", (arg, styleStack) -> myConfig.get(arg));

registerTemplateAsBuiltin takes a ResourceLocation on 1.21.1 and an Identifier on 26.2:

1.21.1

// Register a template as an "addon" template available to all books.
api.registerTemplateAsBuiltin(
        ResourceLocation.fromNamespaceAndPath("mymod", "card"),
        () -> MyMod.class.getResourceAsStream("/assets/mymod/card.json"));

26.2

// Register a template as an "addon" template available to all books.
api.registerTemplateAsBuiltin(
        Identifier.fromNamespaceAndPath("mymod", "card"),
        () -> MyMod.class.getResourceAsStream("/assets/mymod/card.json"));
  • registerCommand(String name, Function<IStyleStack, String>) replaces $(name).
  • registerFunction(String name, BiFunction<String, IStyleStack, String>) replaces $(name:argument).
  • registerTemplateAsBuiltin(ResourceLocation res, Supplier<InputStream>) (1.21.1) / registerTemplateAsBuiltin(Identifier res, Supplier<InputStream>) (26.2) makes a template available to every book under res.

Both command registration methods are client-side and thread safe. The returned string replaces the command; return "" for commands that only modify style.

IComponentProcessor

Identical in both versions.

Implement this on a plain class with a no-arg constructor and name it in a template's processor field. One instance is created per usage of the template.

package com.example.mymod;

import com.skd.vellumli.api.IComponentProcessor;
import com.skd.vellumli.api.IVariable;
import com.skd.vellumli.api.IVariableProvider;
import net.minecraft.world.level.Level;

public class MyProcessor implements IComponentProcessor {
    private IVariableProvider variables;

    @Override
    public void setup(Level level, IVariableProvider variables) {
        this.variables = variables;
    }

    @Override
    public IVariable process(Level level, String key) {
        if (key.equals("greeting")) {
            return IVariable.wrap("Hello from Java!", level.registryAccess());
        }
        return null; // let the default lookup handle it
    }

    // Hide an entire group when it should not be shown.
    @Override
    public boolean allowRender(String group) {
        return !group.equals("debug");
    }
}

setup receives the template's variables (including using bindings). process is called for #keys used in components. allowRender(group) is called for components with a non-empty group. refresh(parent, left, top) is called when the page is shown.

ICustomComponent

Implement this for a fully custom component and reference it with a vellumli:custom component's class field. Fields are normally deserialized from the component JSON, so mark non-JSON fields transient.

The render method differs: 1.21.1 has render(GuiGraphics, ...), 26.2 has extractRenderState(GuiGraphicsExtractor, ...).

1.21.1

package com.example.mymod;

import com.skd.vellumli.api.ICustomComponent;
import com.skd.vellumli.api.IComponentRenderContext;
import com.skd.vellumli.api.IVariable;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.HolderLookup;

import java.util.function.UnaryOperator;

public class MyWidget implements ICustomComponent {
    public IVariable label;

    @Override
    public void onVariablesAvailable(UnaryOperator<IVariable> lookup, HolderLookup.Provider registries) {
        label = lookup.apply(label);
    }

    @Override
    public void build(int componentX, int componentY, int pageNum) {
        // Called after variables are resolved.
    }

    @Override
    public void render(GuiGraphics graphics, IComponentRenderContext context, float pticks, int mouseX, int mouseY) {
        // Draw in book-page coordinates.
    }
}

26.2

package com.example.mymod;

import com.skd.vellumli.api.ICustomComponent;
import com.skd.vellumli.api.IComponentRenderContext;
import com.skd.vellumli.api.IVariable;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.core.HolderLookup;

import java.util.function.UnaryOperator;

public class MyWidget implements ICustomComponent {
    public IVariable label;

    @Override
    public void onVariablesAvailable(UnaryOperator<IVariable> lookup, HolderLookup.Provider registries) {
        label = lookup.apply(label);
    }

    @Override
    public void build(int componentX, int componentY, int pageNum) {
        // Called after variables are resolved.
    }

    @Override
    public void extractRenderState(GuiGraphicsExtractor graphics, IComponentRenderContext context, float pticks, int mouseX, int mouseY) {
        // Draw in book-page coordinates.
    }
}
1.21.1 26.2 Description
void render(GuiGraphics graphics, IComponentRenderContext context, float pticks, int mouseX, int mouseY) void extractRenderState(GuiGraphicsExtractor graphics, IComponentRenderContext context, float pticks, int mouseX, int mouseY) Called every render tick.
default boolean mouseClicked(IComponentRenderContext context, double mouseX, double mouseY, int mouseButton) default boolean mouseClicked(IComponentRenderContext context, MouseButtonEvent event, boolean doubleClick) Called on mouse click.
void build(int componentX, int componentY, int pageNum) identical Called after variables are resolved.
default void onDisplayed(IComponentRenderContext context) identical Called when the component enters the screen.

onDisplayed(context) is called when the component enters the screen. The click handler signature changes in 26.2 to receive a MouseButtonEvent and a double-click flag.

IComponentRenderContext

A context for a custom component's methods. Members whose signature differs:

1.21.1 26.2 Description
Style getFont() Style getFontStyle() The book font style.
void renderItemStack(GuiGraphics graphics, int x, int y, int mouseX, int mouseY, ItemStack stack) void renderItemStack(GuiGraphicsExtractor graphics, int x, int y, int mouseX, int mouseY, ItemStack stack) Draw an item stack.
void renderIngredient(GuiGraphics graphics, int x, int y, int mouseX, int mouseY, Ingredient ingredient) void renderIngredient(GuiGraphicsExtractor graphics, int x, int y, int mouseX, int mouseY, Ingredient ingredient) Draw an ingredient.
boolean navigateToEntry(ResourceLocation entry, int page, boolean push) boolean navigateToEntry(Identifier entry, int page, boolean push) Navigate to another entry.
ResourceLocation getBookTexture() Identifier getBookTexture() The book background texture.
ResourceLocation getCraftingTexture() Identifier getCraftingTexture() The crafting texture.

getGui(), isAreaHovered(...), setHoverTooltip(...), setHoverTooltipComponents(...), registerButton(...), addWidget(...), getTextColor(), getHeaderColor() and getTicksInBook() are identical in both versions.

Variables

Identical in both versions.

IVariable is a JSON-backed value with converters:

IVariable v = IVariable.wrap("hello", level.registryAccess());
String s = v.asString();
ItemStack stack = v.as(ItemStack.class);

Register custom serializers with VariableHelper:

VariableHelper.instance().registerSerializer(new MySerializer(), MyType.class);

An IVariableSerializer<T> converts between T and JsonElement. Implement IVariablesAvailableCallback to receive a lookup function that resolves variable strings (inline #vars, ->derivations and plain values) before your component is built.

Events

Both events extend NeoForge's Event and are posted on NeoForge.EVENT_BUS.

1.21.1 26.2
BookContentsReloadEvent.getBook() returns ResourceLocation BookContentsReloadEvent.getBook() returns Identifier
BookDrawScreenEvent.getBook() returns ResourceLocation BookDrawScreenEvent.getBook() returns Identifier
BookDrawScreenEvent.getGraphics() returns GuiGraphics BookDrawScreenEvent.getGraphics() returns GuiGraphicsExtractor
BookDrawScreenEvent.getScreen(), getMouseX(), getMouseY(), getPartialTicks() identical
import net.neoforged.neoforge.common.NeoForge;
import com.skd.vellumli.api.BookContentsReloadEvent;
import com.skd.vellumli.api.BookDrawScreenEvent;

NeoForge.EVENT_BUS.addListener((BookContentsReloadEvent e) -> {
    // a book's contents were (re)built; e.getBook() is the book id
});

NeoForge.EVENT_BUS.addListener((BookDrawScreenEvent e) -> {
    // a book GUI finished drawing, with the book GUI transform applied:
    // e.getBook(), e.getScreen(), e.getMouseX(), e.getMouseY(),
    // e.getPartialTicks(), e.getGraphics()
});

Multiblocks

The multiblock API methods are present for source compatibility but are stubs in this build: getMultiblock, registerMultiblock, getCurrentMultiblock, showMultiblock, clearMultiblock, makeMultiblock, makeSparseMultiblock and the various matcher factories currently return null or do nothing. The in-world multiblock preview system and the vellumli:multiblock page type are not ported.

Only the id type differs:

1.21.1 26.2
IMultiblock getMultiblock(ResourceLocation id) IMultiblock getMultiblock(Identifier id)
IMultiblock registerMultiblock(ResourceLocation id, IMultiblock mb) IMultiblock registerMultiblock(Identifier id, IMultiblock mb)
IMultiblock setId(ResourceLocation res) IMultiblock setId(Identifier res)
ResourceLocation IMultiblock.getID() Identifier IMultiblock.getID()

The matcher factory methods (predicateMatcher, stateMatcher, propertyMatcher, looseBlockMatcher, strictBlockMatcher, displayOnlyMatcher, tagMatcher, airMatcher, anyMatcher) have identical signatures in both versions.

Clone this wiki locally