or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audience-system.mdbooks-and-inventory.mdboss-bars.mdevents-and-interactivity.mdindex.mdnbt-data-components.mdresource-packs.mdsound-system.mdtext-components.mdtext-formatting.mdtitles-and-subtitles.mdtranslation-system.md

books-and-inventory.mddocs/

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