CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-net-kyori--adventure-api

A serverside user interface library for Minecraft: Java Edition

Pending
Overview
Eval results
Files

books-and-inventory.mddocs/

Books and Inventory

Book creation and management for opening written books with multiple pages, authors, and titles. Adventure provides comprehensive book handling for interactive content delivery.

Capabilities

Book Interface

Core interface for creating and managing written books that can be opened by players.

/**
 * Represents a book with title, author, and pages
 */
interface Book extends Examinable {
    /**
     * Gets the book title
     * @return the title component
     */
    Component title();
    
    /**
     * Gets the book author
     * @return the author component
     */
    Component author();
    
    /**
     * Gets the book pages
     * @return list of page components
     */
    List<Component> pages();
    
    /**
     * Sets the book title
     * @param title the new title
     * @return book with new title
     */
    Book title(ComponentLike title);
    
    /**
     * Sets the book author
     * @param author the new author
     * @return book with new author
     */
    Book author(ComponentLike author);
    
    /**
     * Sets the book pages
     * @param pages the new pages
     * @return book with new pages
     */
    Book pages(List<? extends ComponentLike> pages);
    
    /**
     * Sets the book pages from varargs
     * @param pages the pages
     * @return book with new pages
     */
    Book pages(ComponentLike... pages);
    
    /**
     * Adds a page to the book
     * @param page the page to add
     * @return book with added page
     */
    Book addPage(ComponentLike page);
    
    /**
     * Creates a book with title, author, and pages
     * @param title the book title
     * @param author the book author
     * @param pages the book pages
     * @return new book
     */
    static Book book(ComponentLike title, ComponentLike author, ComponentLike... pages);
    
    /**
     * Creates a book with title, author, and page list
     * @param title the book title
     * @param author the book author
     * @param pages the book pages
     * @return new book
     */
    static Book book(ComponentLike title, ComponentLike author, List<? extends ComponentLike> pages);
    
    /**
     * Creates a book builder
     * @return new book builder
     */
    static Builder book();
    
    /**
     * Builder for creating books
     */
    interface Builder extends AbstractBuilder<Book> {
        /**
         * Sets the book title
         * @param title the title
         * @return this builder
         */
        Builder title(ComponentLike title);
        
        /**
         * Sets the book author
         * @param author the author
         * @return this builder
         */
        Builder author(ComponentLike author);
        
        /**
         * Sets the book pages
         * @param pages the pages
         * @return this builder
         */
        Builder pages(List<? extends ComponentLike> pages);
        
        /**
         * Sets the book pages from varargs
         * @param pages the pages
         * @return this builder
         */
        Builder pages(ComponentLike... pages);
        
        /**
         * Adds a page to the book
         * @param page the page to add
         * @return this builder
         */
        Builder addPage(ComponentLike page);
    }
}

Usage Examples:

import net.kyori.adventure.inventory.Book;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;

// Simple book
Book simpleBook = Book.book(
    Component.text("My Book"),
    Component.text("Author Name"),
    Component.text("This is page 1 of my book."),
    Component.text("This is page 2 with more content.")
);

// Open book for player
audience.openBook(simpleBook);

// Complex book with formatting
Book storyBook = Book.book()
    .title(Component.text("The Adventure", NamedTextColor.GOLD, TextDecoration.BOLD))
    .author(Component.text("Jane Doe", NamedTextColor.BLUE))
    .addPage(Component.text()
        .append(Component.text("Chapter 1", NamedTextColor.DARK_GREEN, TextDecoration.UNDERLINED))
        .appendNewline()
        .appendNewline()
        .append(Component.text("Once upon a time, in a land far away..."))
        .build())
    .addPage(Component.text()
        .append(Component.text("Chapter 2", NamedTextColor.DARK_GREEN, TextDecoration.UNDERLINED))
        .appendNewline()
        .appendNewline()
        .append(Component.text("The hero continued their journey..."))
        .build())
    .build();

audience.openBook(storyBook);

Book Creation Patterns

Interactive Help System

public class HelpBookSystem {
    public Book createHelpBook(String topic) {
        return switch (topic.toLowerCase()) {
            case "commands" -> createCommandsBook();
            case "rules" -> createRulesBook();
            case "features" -> createFeaturesBook();
            default -> createMainHelpBook();
        };
    }
    
    private Book createMainHelpBook() {
        return Book.book()
            .title(Component.text("Server Help", NamedTextColor.BLUE, TextDecoration.BOLD))
            .author(Component.text("Server Staff"))
            .addPage(createTableOfContents())
            .addPage(createGettingStartedPage())
            .addPage(createContactInfoPage())
            .build();
    }
    
    private Component createTableOfContents() {
        return Component.text()
            .append(Component.text("Help Topics", NamedTextColor.DARK_BLUE, TextDecoration.BOLD))
            .appendNewline()
            .appendNewline()
            .append(Component.text("• Getting Started", NamedTextColor.BLACK))
            .appendNewline()
            .append(Component.text("• Commands (/help commands)", NamedTextColor.BLUE)
                .clickEvent(ClickEvent.runCommand("/help commands")))
            .appendNewline()
            .append(Component.text("• Rules (/help rules)", NamedTextColor.BLUE)
                .clickEvent(ClickEvent.runCommand("/help rules")))
            .appendNewline()
            .append(Component.text("• Features (/help features)", NamedTextColor.BLUE)
                .clickEvent(ClickEvent.runCommand("/help features")))
            .build();
    }
    
    private Book createCommandsBook() {
        List<Component> pages = new ArrayList<>();
        
        // Command categories
        pages.add(createCommandCategoryPage("Basic Commands", getBasicCommands()));
        pages.add(createCommandCategoryPage("Economy Commands", getEconomyCommands()));
        pages.add(createCommandCategoryPage("Social Commands", getSocialCommands()));
        
        return Book.book()
            .title(Component.text("Command Reference"))
            .author(Component.text("Server"))
            .pages(pages)
            .build();
    }
    
    private Component createCommandCategoryPage(String category, List<CommandInfo> commands) {
        Component.Builder page = Component.text()
            .append(Component.text(category, NamedTextColor.DARK_GREEN, TextDecoration.BOLD))
            .appendNewline()
            .appendNewline();
        
        for (CommandInfo cmd : commands) {
            page.append(Component.text("/" + cmd.name(), NamedTextColor.BLUE)
                    .clickEvent(ClickEvent.suggestCommand("/" + cmd.name())))
                .appendNewline()
                .append(Component.text(cmd.description(), NamedTextColor.BLACK))
                .appendNewline()
                .appendNewline();
        }
        
        return page.build();
    }
}

Story and Lore Books

public class LoreBookManager {
    private final Map<String, Book> loreBooks = new HashMap<>();
    
    public void createLoreCollection() {
        // Create interconnected story books
        loreBooks.put("origin", createOriginStory());
        loreBooks.put("prophecy", createProphecyBook());
        loreBooks.put("heroes", createHeroesBook());
        loreBooks.put("bestiary", createBestiaryBook());
    }
    
    private Book createOriginStory() {
        return Book.book()
            .title(Component.text("The Origin", NamedTextColor.GOLD, TextDecoration.ITALIC))
            .author(Component.text("Ancient Scribe"))
            .addPage(createStoryPage(
                "In the beginning...",
                "Long before the great kingdoms rose, there was only the Void. " +
                "From this emptiness came the first spark of creation, " +
                "giving birth to the world as we know it."
            ))
            .addPage(createStoryPage(
                "The First Age",
                "The First Age saw the rise of the Elder Beings, " +
                "creatures of immense power who shaped the very fabric of reality. " +
                "Their wars scarred the land and created the great rifts " +
                "that still exist today."
            ))
            .addPage(createNavigationPage())
            .build();
    }
    
    private Component createStoryPage(String title, String content) {
        return Component.text()
            .append(Component.text(title, NamedTextColor.DARK_PURPLE, TextDecoration.BOLD))
            .appendNewline()
            .appendNewline()
            .append(Component.text(content, NamedTextColor.BLACK))
            .build();
    }
    
    private Component createNavigationPage() {
        return Component.text()
            .append(Component.text("Related Stories", NamedTextColor.BLUE, TextDecoration.UNDERLINED))
            .appendNewline()
            .appendNewline()
            .append(Component.text("• The Prophecy", NamedTextColor.DARK_BLUE)
                .clickEvent(ClickEvent.runCommand("/lore prophecy"))
                .hoverEvent(HoverEvent.showText(Component.text("Click to read the ancient prophecy"))))
            .appendNewline()
            .append(Component.text("• Heroes of Old", NamedTextColor.DARK_BLUE)
                .clickEvent(ClickEvent.runCommand("/lore heroes"))
                .hoverEvent(HoverEvent.showText(Component.text("Learn about legendary heroes"))))
            .appendNewline()
            .append(Component.text("• Bestiary", NamedTextColor.DARK_BLUE)
                .clickEvent(ClickEvent.runCommand("/lore bestiary"))
                .hoverEvent(HoverEvent.showText(Component.text("Catalog of creatures"))))
            .build();
    }
}

Dynamic Content Books

public class DynamicBookGenerator {
    public Book createPlayerStatsBook(PlayerStats stats) {
        return Book.book()
            .title(Component.text("Player Statistics"))
            .author(Component.text("Server"))
            .addPage(createStatsOverviewPage(stats))
            .addPage(createAchievementsPage(stats))
            .addPage(createLeaderboardPage(stats))
            .build();
    }
    
    private Component createStatsOverviewPage(PlayerStats stats) {
        return Component.text()
            .append(Component.text("Your Statistics", NamedTextColor.DARK_GREEN, TextDecoration.BOLD))
            .appendNewline().appendNewline()
            .append(createStatLine("Play Time", formatDuration(stats.playTime())))
            .append(createStatLine("Blocks Broken", String.valueOf(stats.blocksBroken())))
            .append(createStatLine("Distance Walked", stats.distanceWalked() + " blocks"))
            .append(createStatLine("Mobs Killed", String.valueOf(stats.mobsKilled())))
            .append(createStatLine("Deaths", String.valueOf(stats.deaths())))
            .append(createStatLine("Level", String.valueOf(stats.level())))
            .build();
    }
    
    private Component createStatLine(String label, String value) {
        return Component.text()
            .append(Component.text(label + ": ", NamedTextColor.GRAY))
            .append(Component.text(value, NamedTextColor.WHITE))
            .appendNewline();
    }
    
    public Book createServerInfoBook(ServerInfo info) {
        return Book.book()
            .title(Component.text("Server Information"))
            .author(Component.text("Administration"))
            .addPage(createServerStatsPage(info))
            .addPage(createOnlinePlayersPage(info))
            .addPage(createServerRulesPage(info))
            .build();
    }
    
    private Component createOnlinePlayersPage(ServerInfo info) {
        Component.Builder page = Component.text()
            .append(Component.text("Online Players", NamedTextColor.BLUE, TextDecoration.BOLD))
            .appendNewline()
            .append(Component.text(info.onlineCount() + "/" + info.maxPlayers(), NamedTextColor.GREEN))
            .appendNewline().appendNewline();
        
        for (String player : info.onlinePlayers()) {
            page.append(Component.text("• " + player, NamedTextColor.YELLOW)
                    .clickEvent(ClickEvent.suggestCommand("/msg " + player + " "))
                    .hoverEvent(HoverEvent.showText(Component.text("Click to message " + player))))
                .appendNewline();
        }
        
        return page.build();
    }
}

Book Validation and Security

public class BookValidator {
    private static final int MAX_PAGE_LENGTH = 256;
    private static final int MAX_PAGES = 50;
    private static final int MAX_TITLE_LENGTH = 32;
    private static final int MAX_AUTHOR_LENGTH = 32;
    
    public boolean validateBook(Book book) {
        // Validate title length
        if (getPlainTextLength(book.title()) > MAX_TITLE_LENGTH) {
            return false;
        }
        
        // Validate author length
        if (getPlainTextLength(book.author()) > MAX_AUTHOR_LENGTH) {
            return false;
        }
        
        // Validate page count
        if (book.pages().size() > MAX_PAGES) {
            return false;
        }
        
        // Validate each page
        for (Component page : book.pages()) {
            if (getPlainTextLength(page) > MAX_PAGE_LENGTH) {
                return false;
            }
            
            if (!validatePageContent(page)) {
                return false;
            }
        }
        
        return true;
    }
    
    private boolean validatePageContent(Component page) {
        // Check for malicious content, inappropriate links, etc.
        PlainTextComponentSerializer serializer = PlainTextComponentSerializer.plainText();
        String plainText = serializer.serialize(page);
        
        // Basic content validation
        return !containsInappropriateContent(plainText) && 
               !containsMaliciousLinks(page);
    }
    
    private int getPlainTextLength(Component component) {
        return PlainTextComponentSerializer.plainText().serialize(component).length();
    }
    
    public Book sanitizeBook(Book book) {
        Component sanitizedTitle = sanitizeComponent(book.title(), MAX_TITLE_LENGTH);
        Component sanitizedAuthor = sanitizeComponent(book.author(), MAX_AUTHOR_LENGTH);
        
        List<Component> sanitizedPages = book.pages().stream()
            .limit(MAX_PAGES)
            .map(page -> sanitizeComponent(page, MAX_PAGE_LENGTH))
            .collect(Collectors.toList());
        
        return Book.book()
            .title(sanitizedTitle)
            .author(sanitizedAuthor)
            .pages(sanitizedPages)
            .build();
    }
    
    private Component sanitizeComponent(Component component, int maxLength) {
        String plainText = PlainTextComponentSerializer.plainText().serialize(component);
        if (plainText.length() <= maxLength) {
            return component;
        }
        
        // Truncate while preserving formatting
        return Component.text(plainText.substring(0, maxLength - 3) + "...")
            .mergeStyle(component);
    }
}

Book UI Patterns

Paginated Content

public class PaginatedBookBuilder {
    private static final int LINES_PER_PAGE = 14;
    private static final int CHARS_PER_LINE = 20; // Approximate
    
    public Book createPaginatedBook(String title, String author, List<String> content) {
        List<Component> pages = new ArrayList<>();
        Component.Builder currentPage = Component.text();
        int currentLines = 0;
        
        for (String line : content) {
            if (currentLines >= LINES_PER_PAGE) {
                pages.add(currentPage.build());
                currentPage = Component.text();
                currentLines = 0;
            }
            
            // Handle long lines by wrapping
            List<String> wrappedLines = wrapText(line, CHARS_PER_LINE);
            for (String wrappedLine : wrappedLines) {
                if (currentLines >= LINES_PER_PAGE) {
                    pages.add(currentPage.build());
                    currentPage = Component.text();
                    currentLines = 0;
                }
                
                currentPage.append(Component.text(wrappedLine)).appendNewline();
                currentLines++;
            }
        }
        
        if (currentLines > 0) {
            pages.add(currentPage.build());
        }
        
        return Book.book()
            .title(Component.text(title))
            .author(Component.text(author))
            .pages(pages)
            .build();
    }
    
    private List<String> wrapText(String text, int maxWidth) {
        List<String> lines = new ArrayList<>();
        String[] words = text.split(" ");
        StringBuilder currentLine = new StringBuilder();
        
        for (String word : words) {
            if (currentLine.length() + word.length() + 1 > maxWidth) {
                if (currentLine.length() > 0) {
                    lines.add(currentLine.toString());
                    currentLine = new StringBuilder();
                }
            }
            
            if (currentLine.length() > 0) {
                currentLine.append(" ");
            }
            currentLine.append(word);
        }
        
        if (currentLine.length() > 0) {
            lines.add(currentLine.toString());
        }
        
        return lines;
    }
}

Best Practices

Content Design

  • Keep page content concise and readable
  • Use appropriate formatting to improve readability
  • Include navigation aids for multi-page books
  • Test books with different client UI scales

Interactive Elements

  • Use click events for navigation between related books
  • Provide hover tooltips for interactive elements
  • Include command suggestions for user actions
  • Create cross-references between related content

Performance and Limits

  • Respect Minecraft's book size limitations
  • Cache frequently accessed books
  • Validate user-generated book content
  • Consider server performance when opening books for many players

User Experience

  • Provide clear titles and authors for identification
  • Use consistent formatting across book collections
  • Include table of contents for longer books
  • Offer multiple ways to access the same information

Install with Tessl CLI

npx tessl i tessl/maven-net-kyori--adventure-api

docs

audience-system.md

books-and-inventory.md

boss-bars.md

events-and-interactivity.md

index.md

nbt-data-components.md

resource-packs.md

sound-system.md

text-components.md

text-formatting.md

titles-and-subtitles.md

translation-system.md

tile.json