0
# Books and Inventory
1
2
Book creation and management for opening written books with multiple pages, authors, and titles. Adventure provides comprehensive book handling for interactive content delivery.
3
4
## Capabilities
5
6
### Book Interface
7
8
Core interface for creating and managing written books that can be opened by players.
9
10
```java { .api }
11
/**
12
* Represents a book with title, author, and pages
13
*/
14
interface Book extends Examinable {
15
/**
16
* Gets the book title
17
* @return the title component
18
*/
19
Component title();
20
21
/**
22
* Gets the book author
23
* @return the author component
24
*/
25
Component author();
26
27
/**
28
* Gets the book pages
29
* @return list of page components
30
*/
31
List<Component> pages();
32
33
/**
34
* Sets the book title
35
* @param title the new title
36
* @return book with new title
37
*/
38
Book title(ComponentLike title);
39
40
/**
41
* Sets the book author
42
* @param author the new author
43
* @return book with new author
44
*/
45
Book author(ComponentLike author);
46
47
/**
48
* Sets the book pages
49
* @param pages the new pages
50
* @return book with new pages
51
*/
52
Book pages(List<? extends ComponentLike> pages);
53
54
/**
55
* Sets the book pages from varargs
56
* @param pages the pages
57
* @return book with new pages
58
*/
59
Book pages(ComponentLike... pages);
60
61
/**
62
* Adds a page to the book
63
* @param page the page to add
64
* @return book with added page
65
*/
66
Book addPage(ComponentLike page);
67
68
/**
69
* Creates a book with title, author, and pages
70
* @param title the book title
71
* @param author the book author
72
* @param pages the book pages
73
* @return new book
74
*/
75
static Book book(ComponentLike title, ComponentLike author, ComponentLike... pages);
76
77
/**
78
* Creates a book with title, author, and page list
79
* @param title the book title
80
* @param author the book author
81
* @param pages the book pages
82
* @return new book
83
*/
84
static Book book(ComponentLike title, ComponentLike author, List<? extends ComponentLike> pages);
85
86
/**
87
* Creates a book builder
88
* @return new book builder
89
*/
90
static Builder book();
91
92
/**
93
* Builder for creating books
94
*/
95
interface Builder extends AbstractBuilder<Book> {
96
/**
97
* Sets the book title
98
* @param title the title
99
* @return this builder
100
*/
101
Builder title(ComponentLike title);
102
103
/**
104
* Sets the book author
105
* @param author the author
106
* @return this builder
107
*/
108
Builder author(ComponentLike author);
109
110
/**
111
* Sets the book pages
112
* @param pages the pages
113
* @return this builder
114
*/
115
Builder pages(List<? extends ComponentLike> pages);
116
117
/**
118
* Sets the book pages from varargs
119
* @param pages the pages
120
* @return this builder
121
*/
122
Builder pages(ComponentLike... pages);
123
124
/**
125
* Adds a page to the book
126
* @param page the page to add
127
* @return this builder
128
*/
129
Builder addPage(ComponentLike page);
130
}
131
}
132
```
133
134
**Usage Examples:**
135
136
```java
137
import net.kyori.adventure.inventory.Book;
138
import net.kyori.adventure.text.Component;
139
import net.kyori.adventure.text.format.NamedTextColor;
140
import net.kyori.adventure.text.format.TextDecoration;
141
142
// Simple book
143
Book simpleBook = Book.book(
144
Component.text("My Book"),
145
Component.text("Author Name"),
146
Component.text("This is page 1 of my book."),
147
Component.text("This is page 2 with more content.")
148
);
149
150
// Open book for player
151
audience.openBook(simpleBook);
152
153
// Complex book with formatting
154
Book storyBook = Book.book()
155
.title(Component.text("The Adventure", NamedTextColor.GOLD, TextDecoration.BOLD))
156
.author(Component.text("Jane Doe", NamedTextColor.BLUE))
157
.addPage(Component.text()
158
.append(Component.text("Chapter 1", NamedTextColor.DARK_GREEN, TextDecoration.UNDERLINED))
159
.appendNewline()
160
.appendNewline()
161
.append(Component.text("Once upon a time, in a land far away..."))
162
.build())
163
.addPage(Component.text()
164
.append(Component.text("Chapter 2", NamedTextColor.DARK_GREEN, TextDecoration.UNDERLINED))
165
.appendNewline()
166
.appendNewline()
167
.append(Component.text("The hero continued their journey..."))
168
.build())
169
.build();
170
171
audience.openBook(storyBook);
172
```
173
174
## Book Creation Patterns
175
176
### Interactive Help System
177
178
```java
179
public class HelpBookSystem {
180
public Book createHelpBook(String topic) {
181
return switch (topic.toLowerCase()) {
182
case "commands" -> createCommandsBook();
183
case "rules" -> createRulesBook();
184
case "features" -> createFeaturesBook();
185
default -> createMainHelpBook();
186
};
187
}
188
189
private Book createMainHelpBook() {
190
return Book.book()
191
.title(Component.text("Server Help", NamedTextColor.BLUE, TextDecoration.BOLD))
192
.author(Component.text("Server Staff"))
193
.addPage(createTableOfContents())
194
.addPage(createGettingStartedPage())
195
.addPage(createContactInfoPage())
196
.build();
197
}
198
199
private Component createTableOfContents() {
200
return Component.text()
201
.append(Component.text("Help Topics", NamedTextColor.DARK_BLUE, TextDecoration.BOLD))
202
.appendNewline()
203
.appendNewline()
204
.append(Component.text("• Getting Started", NamedTextColor.BLACK))
205
.appendNewline()
206
.append(Component.text("• Commands (/help commands)", NamedTextColor.BLUE)
207
.clickEvent(ClickEvent.runCommand("/help commands")))
208
.appendNewline()
209
.append(Component.text("• Rules (/help rules)", NamedTextColor.BLUE)
210
.clickEvent(ClickEvent.runCommand("/help rules")))
211
.appendNewline()
212
.append(Component.text("• Features (/help features)", NamedTextColor.BLUE)
213
.clickEvent(ClickEvent.runCommand("/help features")))
214
.build();
215
}
216
217
private Book createCommandsBook() {
218
List<Component> pages = new ArrayList<>();
219
220
// Command categories
221
pages.add(createCommandCategoryPage("Basic Commands", getBasicCommands()));
222
pages.add(createCommandCategoryPage("Economy Commands", getEconomyCommands()));
223
pages.add(createCommandCategoryPage("Social Commands", getSocialCommands()));
224
225
return Book.book()
226
.title(Component.text("Command Reference"))
227
.author(Component.text("Server"))
228
.pages(pages)
229
.build();
230
}
231
232
private Component createCommandCategoryPage(String category, List<CommandInfo> commands) {
233
Component.Builder page = Component.text()
234
.append(Component.text(category, NamedTextColor.DARK_GREEN, TextDecoration.BOLD))
235
.appendNewline()
236
.appendNewline();
237
238
for (CommandInfo cmd : commands) {
239
page.append(Component.text("/" + cmd.name(), NamedTextColor.BLUE)
240
.clickEvent(ClickEvent.suggestCommand("/" + cmd.name())))
241
.appendNewline()
242
.append(Component.text(cmd.description(), NamedTextColor.BLACK))
243
.appendNewline()
244
.appendNewline();
245
}
246
247
return page.build();
248
}
249
}
250
```
251
252
### Story and Lore Books
253
254
```java
255
public class LoreBookManager {
256
private final Map<String, Book> loreBooks = new HashMap<>();
257
258
public void createLoreCollection() {
259
// Create interconnected story books
260
loreBooks.put("origin", createOriginStory());
261
loreBooks.put("prophecy", createProphecyBook());
262
loreBooks.put("heroes", createHeroesBook());
263
loreBooks.put("bestiary", createBestiaryBook());
264
}
265
266
private Book createOriginStory() {
267
return Book.book()
268
.title(Component.text("The Origin", NamedTextColor.GOLD, TextDecoration.ITALIC))
269
.author(Component.text("Ancient Scribe"))
270
.addPage(createStoryPage(
271
"In the beginning...",
272
"Long before the great kingdoms rose, there was only the Void. " +
273
"From this emptiness came the first spark of creation, " +
274
"giving birth to the world as we know it."
275
))
276
.addPage(createStoryPage(
277
"The First Age",
278
"The First Age saw the rise of the Elder Beings, " +
279
"creatures of immense power who shaped the very fabric of reality. " +
280
"Their wars scarred the land and created the great rifts " +
281
"that still exist today."
282
))
283
.addPage(createNavigationPage())
284
.build();
285
}
286
287
private Component createStoryPage(String title, String content) {
288
return Component.text()
289
.append(Component.text(title, NamedTextColor.DARK_PURPLE, TextDecoration.BOLD))
290
.appendNewline()
291
.appendNewline()
292
.append(Component.text(content, NamedTextColor.BLACK))
293
.build();
294
}
295
296
private Component createNavigationPage() {
297
return Component.text()
298
.append(Component.text("Related Stories", NamedTextColor.BLUE, TextDecoration.UNDERLINED))
299
.appendNewline()
300
.appendNewline()
301
.append(Component.text("• The Prophecy", NamedTextColor.DARK_BLUE)
302
.clickEvent(ClickEvent.runCommand("/lore prophecy"))
303
.hoverEvent(HoverEvent.showText(Component.text("Click to read the ancient prophecy"))))
304
.appendNewline()
305
.append(Component.text("• Heroes of Old", NamedTextColor.DARK_BLUE)
306
.clickEvent(ClickEvent.runCommand("/lore heroes"))
307
.hoverEvent(HoverEvent.showText(Component.text("Learn about legendary heroes"))))
308
.appendNewline()
309
.append(Component.text("• Bestiary", NamedTextColor.DARK_BLUE)
310
.clickEvent(ClickEvent.runCommand("/lore bestiary"))
311
.hoverEvent(HoverEvent.showText(Component.text("Catalog of creatures"))))
312
.build();
313
}
314
}
315
```
316
317
### Dynamic Content Books
318
319
```java
320
public class DynamicBookGenerator {
321
public Book createPlayerStatsBook(PlayerStats stats) {
322
return Book.book()
323
.title(Component.text("Player Statistics"))
324
.author(Component.text("Server"))
325
.addPage(createStatsOverviewPage(stats))
326
.addPage(createAchievementsPage(stats))
327
.addPage(createLeaderboardPage(stats))
328
.build();
329
}
330
331
private Component createStatsOverviewPage(PlayerStats stats) {
332
return Component.text()
333
.append(Component.text("Your Statistics", NamedTextColor.DARK_GREEN, TextDecoration.BOLD))
334
.appendNewline().appendNewline()
335
.append(createStatLine("Play Time", formatDuration(stats.playTime())))
336
.append(createStatLine("Blocks Broken", String.valueOf(stats.blocksBroken())))
337
.append(createStatLine("Distance Walked", stats.distanceWalked() + " blocks"))
338
.append(createStatLine("Mobs Killed", String.valueOf(stats.mobsKilled())))
339
.append(createStatLine("Deaths", String.valueOf(stats.deaths())))
340
.append(createStatLine("Level", String.valueOf(stats.level())))
341
.build();
342
}
343
344
private Component createStatLine(String label, String value) {
345
return Component.text()
346
.append(Component.text(label + ": ", NamedTextColor.GRAY))
347
.append(Component.text(value, NamedTextColor.WHITE))
348
.appendNewline();
349
}
350
351
public Book createServerInfoBook(ServerInfo info) {
352
return Book.book()
353
.title(Component.text("Server Information"))
354
.author(Component.text("Administration"))
355
.addPage(createServerStatsPage(info))
356
.addPage(createOnlinePlayersPage(info))
357
.addPage(createServerRulesPage(info))
358
.build();
359
}
360
361
private Component createOnlinePlayersPage(ServerInfo info) {
362
Component.Builder page = Component.text()
363
.append(Component.text("Online Players", NamedTextColor.BLUE, TextDecoration.BOLD))
364
.appendNewline()
365
.append(Component.text(info.onlineCount() + "/" + info.maxPlayers(), NamedTextColor.GREEN))
366
.appendNewline().appendNewline();
367
368
for (String player : info.onlinePlayers()) {
369
page.append(Component.text("• " + player, NamedTextColor.YELLOW)
370
.clickEvent(ClickEvent.suggestCommand("/msg " + player + " "))
371
.hoverEvent(HoverEvent.showText(Component.text("Click to message " + player))))
372
.appendNewline();
373
}
374
375
return page.build();
376
}
377
}
378
```
379
380
### Book Validation and Security
381
382
```java
383
public class BookValidator {
384
private static final int MAX_PAGE_LENGTH = 256;
385
private static final int MAX_PAGES = 50;
386
private static final int MAX_TITLE_LENGTH = 32;
387
private static final int MAX_AUTHOR_LENGTH = 32;
388
389
public boolean validateBook(Book book) {
390
// Validate title length
391
if (getPlainTextLength(book.title()) > MAX_TITLE_LENGTH) {
392
return false;
393
}
394
395
// Validate author length
396
if (getPlainTextLength(book.author()) > MAX_AUTHOR_LENGTH) {
397
return false;
398
}
399
400
// Validate page count
401
if (book.pages().size() > MAX_PAGES) {
402
return false;
403
}
404
405
// Validate each page
406
for (Component page : book.pages()) {
407
if (getPlainTextLength(page) > MAX_PAGE_LENGTH) {
408
return false;
409
}
410
411
if (!validatePageContent(page)) {
412
return false;
413
}
414
}
415
416
return true;
417
}
418
419
private boolean validatePageContent(Component page) {
420
// Check for malicious content, inappropriate links, etc.
421
PlainTextComponentSerializer serializer = PlainTextComponentSerializer.plainText();
422
String plainText = serializer.serialize(page);
423
424
// Basic content validation
425
return !containsInappropriateContent(plainText) &&
426
!containsMaliciousLinks(page);
427
}
428
429
private int getPlainTextLength(Component component) {
430
return PlainTextComponentSerializer.plainText().serialize(component).length();
431
}
432
433
public Book sanitizeBook(Book book) {
434
Component sanitizedTitle = sanitizeComponent(book.title(), MAX_TITLE_LENGTH);
435
Component sanitizedAuthor = sanitizeComponent(book.author(), MAX_AUTHOR_LENGTH);
436
437
List<Component> sanitizedPages = book.pages().stream()
438
.limit(MAX_PAGES)
439
.map(page -> sanitizeComponent(page, MAX_PAGE_LENGTH))
440
.collect(Collectors.toList());
441
442
return Book.book()
443
.title(sanitizedTitle)
444
.author(sanitizedAuthor)
445
.pages(sanitizedPages)
446
.build();
447
}
448
449
private Component sanitizeComponent(Component component, int maxLength) {
450
String plainText = PlainTextComponentSerializer.plainText().serialize(component);
451
if (plainText.length() <= maxLength) {
452
return component;
453
}
454
455
// Truncate while preserving formatting
456
return Component.text(plainText.substring(0, maxLength - 3) + "...")
457
.mergeStyle(component);
458
}
459
}
460
```
461
462
## Book UI Patterns
463
464
### Paginated Content
465
466
```java
467
public class PaginatedBookBuilder {
468
private static final int LINES_PER_PAGE = 14;
469
private static final int CHARS_PER_LINE = 20; // Approximate
470
471
public Book createPaginatedBook(String title, String author, List<String> content) {
472
List<Component> pages = new ArrayList<>();
473
Component.Builder currentPage = Component.text();
474
int currentLines = 0;
475
476
for (String line : content) {
477
if (currentLines >= LINES_PER_PAGE) {
478
pages.add(currentPage.build());
479
currentPage = Component.text();
480
currentLines = 0;
481
}
482
483
// Handle long lines by wrapping
484
List<String> wrappedLines = wrapText(line, CHARS_PER_LINE);
485
for (String wrappedLine : wrappedLines) {
486
if (currentLines >= LINES_PER_PAGE) {
487
pages.add(currentPage.build());
488
currentPage = Component.text();
489
currentLines = 0;
490
}
491
492
currentPage.append(Component.text(wrappedLine)).appendNewline();
493
currentLines++;
494
}
495
}
496
497
if (currentLines > 0) {
498
pages.add(currentPage.build());
499
}
500
501
return Book.book()
502
.title(Component.text(title))
503
.author(Component.text(author))
504
.pages(pages)
505
.build();
506
}
507
508
private List<String> wrapText(String text, int maxWidth) {
509
List<String> lines = new ArrayList<>();
510
String[] words = text.split(" ");
511
StringBuilder currentLine = new StringBuilder();
512
513
for (String word : words) {
514
if (currentLine.length() + word.length() + 1 > maxWidth) {
515
if (currentLine.length() > 0) {
516
lines.add(currentLine.toString());
517
currentLine = new StringBuilder();
518
}
519
}
520
521
if (currentLine.length() > 0) {
522
currentLine.append(" ");
523
}
524
currentLine.append(word);
525
}
526
527
if (currentLine.length() > 0) {
528
lines.add(currentLine.toString());
529
}
530
531
return lines;
532
}
533
}
534
```
535
536
## Best Practices
537
538
### Content Design
539
- Keep page content concise and readable
540
- Use appropriate formatting to improve readability
541
- Include navigation aids for multi-page books
542
- Test books with different client UI scales
543
544
### Interactive Elements
545
- Use click events for navigation between related books
546
- Provide hover tooltips for interactive elements
547
- Include command suggestions for user actions
548
- Create cross-references between related content
549
550
### Performance and Limits
551
- Respect Minecraft's book size limitations
552
- Cache frequently accessed books
553
- Validate user-generated book content
554
- Consider server performance when opening books for many players
555
556
### User Experience
557
- Provide clear titles and authors for identification
558
- Use consistent formatting across book collections
559
- Include table of contents for longer books
560
- Offer multiple ways to access the same information