papermc-skill 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +12 -12
  3. package/assets/banner-animated.svg +360 -0
  4. package/assets/banner.svg +360 -0
  5. package/bin/cli.js +107 -2
  6. package/docs/01-modern-paper-architecture.md +48 -51
  7. package/docs/02-adventure-and-minimessage.md +48 -52
  8. package/docs/03-persistent-data-container.md +39 -39
  9. package/docs/04-commands-and-brigadier.md +32 -35
  10. package/docs/05-concurrency-and-folia.md +44 -44
  11. package/docs/06-events-and-listeners.md +30 -31
  12. package/docs/07-best-practices-and-perf.md +39 -40
  13. package/docs/08-gui-and-menus.md +34 -37
  14. package/docs/09-custom-items-and-combat.md +25 -33
  15. package/docs/10-database-and-storage.md +27 -27
  16. package/docs/11-security-and-exploit-prevention.md +233 -0
  17. package/package.json +2 -1
  18. package/skills/paper-plugin-dev/SKILL.md +391 -114
  19. package/templates/paper-modern-template/.gradle/9.7.1/checksums/checksums.lock +0 -0
  20. package/templates/paper-modern-template/.gradle/9.7.1/checksums/md5-checksums.bin +0 -0
  21. package/templates/paper-modern-template/.gradle/9.7.1/checksums/sha1-checksums.bin +0 -0
  22. package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.bin +0 -0
  23. package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.lock +0 -0
  24. package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.bin +0 -0
  25. package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.lock +0 -0
  26. package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/resourceHashesCache.bin +0 -0
  27. package/templates/paper-modern-template/.gradle/9.7.1/kotlin-dsl-plugin-entries/kotlin-dsl-plugin-entries.lock +0 -0
  28. package/templates/paper-modern-template/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  29. package/templates/paper-modern-template/.gradle/buildOutputCleanup/cache.properties +1 -1
  30. package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/ExamplePaperPlugin.class +0 -0
  31. package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/PaperPluginBootstrap.class +0 -0
  32. package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/command/HelloCommand.class +0 -0
  33. package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/listener/PlayerEventListener.class +0 -0
  34. package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/util/SchedulerAdapter.class +0 -0
  35. package/templates/paper-modern-template/build/tmp/compileJava/previous-compilation-data.bin +0 -0
  36. package/templates/paper-modern-template/build.gradle.kts +7 -7
  37. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/ExamplePaperPlugin.java +6 -6
  38. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/PaperPluginBootstrap.java +2 -2
  39. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/command/HelloCommand.java +2 -4
  40. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/data/PluginDataKeys.java +2 -2
  41. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/listener/PlayerEventListener.java +6 -7
  42. package/templates/paper-modern-template/src/main/java/com/example/paperplugin/util/SchedulerAdapter.java +6 -6
  43. package/templates/paper-modern-template/src/main/resources/config.yml +5 -5
@@ -1,55 +1,52 @@
1
- # Arsitektur Modern Plugin Paper Minecraft Java
1
+ # Modern Minecraft Java Plugin Architecture (Paper & Folia)
2
2
 
3
- Dokumen ini membahas arsitektur modern pembuatan plugin untuk server Paper Minecraft Java (khususnya versi 1.20+ dan 1.21+), mencakup build tools, konfigurasi manifest, lifecycle bootstrapping, dan sistem pemetaan (Mojang mappings).
3
+ This guide explores the modern architectural patterns for building Minecraft Java plugins on PaperMC (specifically versions 1.20+ and 1.21+), covering build tooling, manifest configuration, lifecycle bootstrapping, and native Mojang mappings.
4
4
 
5
5
  ---
6
6
 
7
- ## 1. Evolusi Arsitektur: Spigot vs Modern Paper
7
+ ## 1. Architectural Evolution: Legacy Spigot vs Modern Paper
8
8
 
9
- Secara historis, plugin Minecraft dibuat di atas Bukkit/Spigot API dengan `plugin.yml` dan Maven/Gradle sederhana. Namun, ekosistem Paper modern telah merevolusi cara kerja plugin:
9
+ Historically, plugins were built on the Bukkit/Spigot API using `plugin.yml` and straightforward Maven/Gradle configurations. However, modern Paper has revolutionized plugin architecture:
10
10
 
11
- | Fitur | Spigot Legacy | Modern Paper (1.20.6+ / 1.21+) |
11
+ | Feature | Legacy Spigot | Modern Paper (1.20.6+ / 1.21+) |
12
12
  | :--- | :--- | :--- |
13
- | **Manifest File** | `plugin.yml` | `paper-plugin.yml` (atau hybrid) |
14
- | **Text Handling** | `ChatColor` & legacy string formatting (`§a`) | Kyori Adventure & MiniMessage (`Component`) |
15
- | **Lifecycle Hook** | Hanya `JavaPlugin.onLoad` & `onEnable` | `PluginBootstrap` + `JavaPlugin` + `LifecycleEvents` |
16
- | **Command System** | `getCommand("...").setExecutor(...)` | Paper Brigadier Native (`LifecycleEvents.COMMANDS`) atau Cloud |
17
- | **Mapping NMS** | Obfuscated Spigot mapping (`reobf`) | Mojang Mappings langsung di runtime (1.20.5+) via Paperweight |
18
- | **Multi-threading** | Single main thread (`BukkitScheduler`) | Multi-thread regionized ready (Folia & Schedulers terpisah) |
13
+ | **Manifest File** | `plugin.yml` | `paper-plugin.yml` (or hybrid) |
14
+ | **Text Handling** | `ChatColor` & legacy formatting (`§a`) | Kyori Adventure & MiniMessage (`Component`) |
15
+ | **Lifecycle Hooks** | Only `JavaPlugin.onLoad` & `onEnable` | `PluginBootstrap` + `JavaPlugin` + `LifecycleEvents` |
16
+ | **Command Engine** | `getCommand("...").setExecutor(...)` | Paper Native Brigadier (`LifecycleEvents.COMMANDS`) or Cloud |
17
+ | **NMS Mappings** | Obfuscated Spigot mappings (`reobf`) | Runtime Mojang Mappings (1.20.5+) via Paperweight |
18
+ | **Multi-Threading** | Single main thread (`BukkitScheduler`) | Multi-threaded region ticking (Folia & region schedulers) |
19
19
 
20
20
  ---
21
21
 
22
22
  ## 2. Build Tooling: Gradle & Paperweight Userdev
23
23
 
24
- Standard industri modern untuk proyek Paper adalah **Gradle (Kotlin DSL - `.gradle.kts`)** menggunakan plugin **`paperweight-userdev`** dari PaperMC.
24
+ The modern industry standard for Paper projects is **Gradle (Kotlin DSL - `.gradle.kts`)** using PaperMC's **`paperweight-userdev`** plugin.
25
25
 
26
- ### Keunggulan Paperweight Userdev:
27
- - Menyediakan akses ke **Mojang Mappings (NMS)** langsung tanpa alat deobfuscation eksternal yang rumit.
28
- - Sejak Paper 1.20.5+, server Paper menjalankan Mojang Mappings secara native di runtime.
29
- - Terintegrasi dengan plugin `xyz.jpenilla.run-paper` untuk menjalankan test server Paper/Folia langsung dengan satu perintah gradle (`./gradlew runServer`).
26
+ ### Key Benefits of Paperweight Userdev:
27
+ - Direct access to **Mojang Mappings (NMS)** without cumbersome external deobfuscation workflows.
28
+ - Starting from Paper 1.20.5+, Paper servers run native Mojang Mappings in production at runtime.
29
+ - Seamless integration with `xyz.jpenilla.run-paper` to launch automated Paper/Folia test servers with a single command (`./gradlew runServer`).
30
30
 
31
- ### Contoh `build.gradle.kts`:
31
+ ### Example `build.gradle.kts`:
32
32
 
33
33
  ```kotlin
34
34
  plugins {
35
35
  `java-library`
36
36
  id("io.papermc.paperweight.userdev") version "2.0.0-beta.21"
37
- id("xyz.jpenilla.run-paper") version "3.0.2" // Task ./gradlew runServer
38
- id("com.gradleup.shadow") version "8.3.6" // Shadow jar bila menggunakan dependensi eksternal
37
+ id("xyz.jpenilla.run-paper") version "3.1.0" // Task: ./gradlew runServer
38
+ id("com.gradleup.shadow") version "8.3.6" // Shadow jar for bundled external libraries
39
39
  }
40
40
 
41
41
  group = "com.example"
42
42
  version = "1.0.0-SNAPSHOT"
43
43
 
44
44
  java {
45
- toolchain.languageVersion.set(JavaLanguageVersion.of(21)) // Minecraft 1.20.5+ membutuhkan minimal Java 21
45
+ toolchain.languageVersion.set(JavaLanguageVersion.of(21)) // Minecraft 1.20.5+ requires Java 21+
46
46
  }
47
47
 
48
48
  dependencies {
49
- paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
50
-
51
- // Contoh library umum
52
- // implementation("org.incendo:cloud-paper:2.0.0-beta.10")
49
+ paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT")
53
50
  }
54
51
 
55
52
  tasks {
@@ -59,8 +56,8 @@ tasks {
59
56
  }
60
57
 
61
58
  shadowJar {
62
- archiveClassifier.set("") // Menjadikan output shadow jar sebagai artifact utama
63
- // relocate package eksternal agar tidak bentrok dengan plugin lain
59
+ archiveClassifier.set("") // Set shaded jar as default artifact output
60
+ // Relocate external packages to prevent classpath collisions:
64
61
  // relocate("org.incendo.cloud", "com.example.plugin.libs.cloud")
65
62
  }
66
63
  }
@@ -68,16 +65,16 @@ tasks {
68
65
 
69
66
  ---
70
67
 
71
- ## 3. Konfigurasi `paper-plugin.yml`
68
+ ## 3. Manifest Configuration: `paper-plugin.yml`
72
69
 
73
- Modern Paper menggunakan manifest `paper-plugin.yml` yang terletak di `src/main/resources/paper-plugin.yml`.
70
+ Modern Paper introduces the `paper-plugin.yml` manifest located at `src/main/resources/paper-plugin.yml`.
74
71
 
75
- ### Keuntungan `paper-plugin.yml`:
76
- - Dukungan dependency loading yang jauh lebih fleksibel (bootstrapper dependencies, server dependencies).
77
- - Mendukung deklarasi bootstrapper class (`bootstrapper`).
78
- - Kompatibilitas multi-threaded Folia (`folia-supported: true`).
72
+ ### Advantages of `paper-plugin.yml`:
73
+ - Granular dependency management (bootstrapper dependencies vs server runtime dependencies).
74
+ - Declarative bootstrapper class definition (`bootstrapper`).
75
+ - Explicit multi-threaded Folia compatibility (`folia-supported: true`).
79
76
 
80
- ### Contoh Struktur `paper-plugin.yml`:
77
+ ### Example `paper-plugin.yml` Structure:
81
78
 
82
79
  ```yaml
83
80
  name: ExamplePlugin
@@ -92,7 +89,7 @@ authors:
92
89
 
93
90
  dependencies:
94
91
  bootstrap:
95
- # Dependensi yang dibutuhkan pada fase bootstrap
92
+ # Dependencies required during the bootstrap phase
96
93
  server:
97
94
  Vault:
98
95
  load: BEFORE
@@ -105,21 +102,21 @@ dependencies:
105
102
 
106
103
  ---
107
104
 
108
- ## 4. Lifecycle Baru: `PluginBootstrap` vs `JavaPlugin`
105
+ ## 4. Modern Lifecycle: `PluginBootstrap` vs `JavaPlugin`
109
106
 
110
- Paper memperkenalkan antarmuka `io.papermc.paper.plugin.bootstrap.PluginBootstrap`.
107
+ Paper introduces the `io.papermc.paper.plugin.bootstrap.PluginBootstrap` interface.
111
108
 
112
- ### Urutan Lifecycle Server Paper:
113
- 1. **Plugin Loader Creation**: Resolusi classpath dan dependensi.
109
+ ### Server Lifecycle Sequence:
110
+ 1. **Plugin Loader Creation**: Classpath resolution and dependency wiring.
114
111
  2. **Bootstrap Phase (`PluginBootstrap#bootstrap`)**:
115
- - Berjalan sangat awal sebelum world di-load.
116
- - Digunakan untuk meregistrasi **Brigadier Commands**, modifikasi konfigurasi registry, dan setup resource global.
117
- 3. **Plugin Instantiation**: Instance `JavaPlugin` dibuat.
118
- 4. **`onLoad()`**: Mirip fase Spigot tradisional.
119
- 5. **`onEnable()`**: Registrasi listener event, scheduler, inisialisasi database, dan setup UI/Game logic.
120
- 6. **`onDisable()`**: Graceful shutdown, flush queue database, dan pembatalan task.
112
+ - Executes before worlds are loaded.
113
+ - Used to register **Brigadier Commands**, modify registry configurations, and set up global resources.
114
+ 3. **Plugin Instantiation**: The `JavaPlugin` instance is created.
115
+ 4. **`onLoad()`**: Equivalent to the legacy Bukkit phase.
116
+ 5. **`onEnable()`**: Event listener registration, schedulers, database connection pools, and UI/game logic setup.
117
+ 6. **`onDisable()`**: Graceful shutdown, database queue flushing, and task cancellation.
121
118
 
122
- ### Contoh Implementasi Bootstrapper:
119
+ ### Bootstrapper Implementation Example:
123
120
 
124
121
  ```java
125
122
  package com.example.plugin;
@@ -134,16 +131,16 @@ public final class ExampleBootstrap implements PluginBootstrap {
134
131
 
135
132
  @Override
136
133
  public void bootstrap(final BootstrapContext context) {
137
- // Registrasi Lifecycle Events seperti command handler modern
134
+ // Register Lifecycle Events such as modern Brigadier command handlers
138
135
  context.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
139
136
  final var registrar = event.registrar();
140
- // Registrasi syntax Brigadier di sini
137
+ // Register Brigadier command nodes here
141
138
  });
142
139
  }
143
140
  }
144
141
  ```
145
142
 
146
- ### Contoh Implementasi Main Plugin (`JavaPlugin`):
143
+ ### Main Plugin Implementation (`JavaPlugin`):
147
144
 
148
145
  ```java
149
146
  package com.example.plugin;
@@ -156,16 +153,16 @@ public final class ExamplePlugin extends JavaPlugin {
156
153
 
157
154
  @Override
158
155
  public void onEnable() {
159
- // Menggunakan ComponentLogger modern Paper
156
+ // Using Paper's modern ComponentLogger
160
157
  getComponentLogger().info(
161
- Component.text("ExamplePlugin berhasil diaktifkan!", NamedTextColor.GREEN)
158
+ Component.text("ExamplePlugin successfully enabled!", NamedTextColor.GREEN)
162
159
  );
163
160
  }
164
161
 
165
162
  @Override
166
163
  public void onDisable() {
167
164
  getComponentLogger().info(
168
- Component.text("ExamplePlugin dimatikan secara aman.", NamedTextColor.RED)
165
+ Component.text("ExamplePlugin safely disabled.", NamedTextColor.RED)
169
166
  );
170
167
  }
171
168
  }
@@ -1,21 +1,21 @@
1
- # Adventure API & MiniMessage di Paper Minecraft
1
+ # Adventure API & MiniMessage in Modern Paper
2
2
 
3
- Paper mengintegrasikan **Kyori Adventure** secara native sebagai antarmuka pemrosesan teks. Pendekatan lama yang menggunakan tanda warna section (`§a`, `§l`) atau `org.bukkit.ChatColor` telah **ditinggalkan (deprecated)** karena rentan merusak format teks, tidak mendukung RGB hex secara fleksibel, dan tidak modular.
3
+ Paper integrates **Kyori Adventure** natively as its primary text interface. Legacy color formatting using section signs (`§a`, `§l`) or `org.bukkit.ChatColor` has been **deprecated** because it corrupts formatting across modern clients, lacks modularity, and cannot express 24-bit TrueColor RGB gradients cleanly.
4
4
 
5
5
  ---
6
6
 
7
- ## 1. Mengapa Adventure & MiniMessage?
7
+ ## 1. Why Kyori Adventure & MiniMessage?
8
8
 
9
- - **Type-Safe Components**: Teks bukan sekadar string biasa melainkan hierarki objek `Component` yang immutable.
10
- - **Dukungan Warna Penuh**: Mendukung warna 24-bit TrueColor / Hex (`#FF5555`), Gradients, dan Rainbows.
11
- - **Interaktivitas Kaya**: Event klik (`run_command`, `suggest_command`, `open_url`), hover tooltip (`show_text`, `show_item`, `show_entity`), dan font kustom.
12
- - **MiniMessage Format**: String format yang sangat mudah dibaca manusia berbasis tag XML-like, aman dari injeksi formatting.
9
+ - **Type-Safe Components**: Text is represented as an immutable hierarchy of `Component` objects rather than brittle strings.
10
+ - **Full TrueColor Support**: Native 24-bit Hex colors (`#FF5555`), dynamic Gradients, and Rainbow transitions.
11
+ - **Rich Interactivity**: Click events (`run_command`, `suggest_command`, `open_url`), hover tooltips (`show_text`, `show_item`, `show_entity`), and custom typography fonts.
12
+ - **MiniMessage Format**: A human-readable XML-like syntax that prevents formatting injection vulnerabilities.
13
13
 
14
14
  ---
15
15
 
16
- ## 2. Format Dasar MiniMessage
16
+ ## 2. Basic MiniMessage Usage
17
17
 
18
- Gunakan `net.kyori.adventure.text.minimessage.MiniMessage`:
18
+ Use `net.kyori.adventure.text.minimessage.MiniMessage`:
19
19
 
20
20
  ```java
21
21
  import net.kyori.adventure.text.Component;
@@ -23,25 +23,25 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
23
23
 
24
24
  final MiniMessage mm = MiniMessage.miniMessage();
25
25
 
26
- // Contoh teks berwarna dan bergradasi
27
- Component c1 = mm.deserialize("<gradient:#ff007f:#7f00ff><bold>Server RPG Terhebat</bold></gradient>");
28
- Component c2 = mm.deserialize("<gray>Klik <green><click:run_command:'/spawn'>DI SINI</click></green> untuk kembali ke spawn.</gray>");
29
- Component c3 = mm.deserialize("<hover:show_text:'<yellow>Status: Aktif</yellow>'>Arahkan kursor ke sini</hover>");
26
+ // Colored and gradient text examples
27
+ Component c1 = mm.deserialize("<gradient:#ff007f:#7f00ff><bold>Legendary RPG Server</bold></gradient>");
28
+ Component c2 = mm.deserialize("<gray>Click <green><click:run_command:'/spawn'>HERE</click></green> to return to spawn.</gray>");
29
+ Component c3 = mm.deserialize("<hover:show_text:'<yellow>Status: Online</yellow>'>Hover over me</hover>");
30
30
  ```
31
31
 
32
- ### Tag MiniMessage Populer:
33
- - Warna bawaan: `<red>`, `<green>`, `<gold>`, `<aqua>`, `<dark_purple>`, `<white>`, dll.
34
- - Warna Hex: `<#rrggbb>` atau `<color:#rrggbb>`.
35
- - Format style: `<bold>` (`<b>`), `<italic>` (`<i>`), `<underlined>` (`<u>`), `<strikethrough>` (`<s>`), `<obfuscated>` (`<obf>`).
36
- - Gradasi warna: `<gradient:#55cdfc:#f7a8b8:#ffffff>Trans Rights</gradient>`.
37
- - Pelangi: `<rainbow>Teks pelangi dinamis</rainbow>`.
38
- - Reset gaya: `<reset>` atau penutupan tag `</red>`.
32
+ ### Popular MiniMessage Tags:
33
+ - Named colors: `<red>`, `<green>`, `<gold>`, `<aqua>`, `<dark_purple>`, `<white>`, etc.
34
+ - Hex colors: `<#rrggbb>` or `<color:#rrggbb>`.
35
+ - Text styling: `<bold>` (`<b>`), `<italic>` (`<i>`), `<underlined>` (`<u>`), `<strikethrough>` (`<s>`), `<obfuscated>` (`<obf>`).
36
+ - Gradients: `<gradient:#55cdfc:#f7a8b8:#ffffff>Trans Rights</gradient>`.
37
+ - Rainbow: `<rainbow>Dynamic rainbow text</rainbow>`.
38
+ - Style resets: `<reset>` or explicit closing tags like `</red>`.
39
39
 
40
40
  ---
41
41
 
42
- ## 3. Dynamic Tag Resolvers (Placeholder Kustom)
42
+ ## 3. Dynamic Tag Resolvers (Custom Placeholders)
43
43
 
44
- MiniMessage menyediakan `TagResolver` untuk memasukkan variabel secara dinamis tanpa merusak parsing atau membuka celah string injection:
44
+ MiniMessage provides `TagResolver` to insert dynamic variables safely without risking formatting injection:
45
45
 
46
46
  ```java
47
47
  import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
@@ -50,7 +50,7 @@ import org.bukkit.entity.Player;
50
50
 
51
51
  public Component createWelcomeMessage(Player player, int coins) {
52
52
  return MiniMessage.miniMessage().deserialize(
53
- "<gold>Selamat datang, <yellow><player_name></yellow>! Koin Anda: <green><coins></green></gold>",
53
+ "<gold>Welcome, <yellow><player_name></yellow>! Balance: <green><coins></green></gold>",
54
54
  TagResolver.resolver(
55
55
  Placeholder.component("player_name", player.displayName()),
56
56
  Placeholder.unparsed("coins", String.valueOf(coins))
@@ -59,28 +59,28 @@ public Component createWelcomeMessage(Player player, int coins) {
59
59
  }
60
60
  ```
61
61
 
62
- > **Perbedaan `component` vs `unparsed`**:
63
- > - `Placeholder.component(...)`: Menyisipkan child component yang sudah di-parse, mempertahankan format.
64
- > - `Placeholder.unparsed(...)`: Menyisipkan teks murni. Tag apapun di dalamnya **tidak** akan di-evaluasi sebagai tag MiniMessage (aman untuk input dari user/chat chatbox).
62
+ > **`component` vs `unparsed`**:
63
+ > - `Placeholder.component(...)`: Inserts an already-parsed child component, preserving rich formatting.
64
+ > - `Placeholder.unparsed(...)`: Inserts raw literal text. Any formatting tags inside are **not** evaluated as MiniMessage tags (crucial for sanitizing chat input).
65
65
 
66
66
  ---
67
67
 
68
- ## 4. Mengirim Pesan ke Audience
68
+ ## 4. Sending Messages to Audiences
69
69
 
70
- Di Paper, setiap entitas yang bisa menerima pesan mengimplementasikan antarmuka `net.kyori.adventure.audience.Audience` (termasuk `Player`, `ConsoleCommandSender`, dan `Server`).
70
+ In Paper, every message receiver implements the `net.kyori.adventure.audience.Audience` interface (including `Player`, `ConsoleCommandSender`, and `Server`).
71
71
 
72
- ### Chat Biasa & Console Logging
72
+ ### Standard Chat & Console Logging
73
73
  ```java
74
- // Mengirim ke pemain
75
- player.sendMessage(MiniMessage.miniMessage().deserialize("<aqua>Pesan masuk!</aqua>"));
74
+ // Send to player
75
+ player.sendMessage(MiniMessage.miniMessage().deserialize("<aqua>Incoming message!</aqua>"));
76
76
 
77
- // Mengirim via ComponentLogger pada plugin
78
- getComponentLogger().info(MiniMessage.miniMessage().deserialize("<green>Plugin siap melayani pemain!</green>"));
77
+ // Send via ComponentLogger on the plugin instance
78
+ getComponentLogger().info(MiniMessage.miniMessage().deserialize("<green>Plugin ready for connections!</green>"));
79
79
  ```
80
80
 
81
81
  ### Action Bar
82
82
  ```java
83
- player.sendActionBar(MiniMessage.miniMessage().deserialize("<red>Zona Bahaya!</red>"));
83
+ player.sendActionBar(MiniMessage.miniMessage().deserialize("<red>Hazard Zone!</red>"));
84
84
  ```
85
85
 
86
86
  ### Titles & Subtitles
@@ -89,8 +89,8 @@ import net.kyori.adventure.title.Title;
89
89
  import java.time.Duration;
90
90
 
91
91
  final Title title = Title.title(
92
- MiniMessage.miniMessage().deserialize("<gold><bold>KEMENANGAN!</bold></gold>"),
93
- MiniMessage.miniMessage().deserialize("<gray>Anda memenangkan ronde ini</gray>"),
92
+ MiniMessage.miniMessage().deserialize("<gold><bold>VICTORY!</bold></gold>"),
93
+ MiniMessage.miniMessage().deserialize("<gray>You won this match</gray>"),
94
94
  Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(1000))
95
95
  );
96
96
 
@@ -102,16 +102,16 @@ player.showTitle(title);
102
102
  import net.kyori.adventure.bossbar.BossBar;
103
103
 
104
104
  BossBar bossBar = BossBar.bossBar(
105
- MiniMessage.miniMessage().deserialize("<dark_red>Ender Dragon HP</dark_red>"),
105
+ MiniMessage.miniMessage().deserialize("<dark_red>Ender Dragon Health</dark_red>"),
106
106
  0.75f, // 75%
107
107
  BossBar.Color.RED,
108
108
  BossBar.Overlay.PROGRESS
109
109
  );
110
110
 
111
- // Tampilkan bossbar
111
+ // Display boss bar
112
112
  player.showBossBar(bossBar);
113
113
 
114
- // Hapus bila sudah selesai
114
+ // Remove when completed
115
115
  player.hideBossBar(bossBar);
116
116
  ```
117
117
 
@@ -132,9 +132,9 @@ player.playSound(sound);
132
132
 
133
133
  ---
134
134
 
135
- ## 5. Serialisasi Item & Lore dengan Adventure
135
+ ## 5. Item Serialization & Lore with Adventure
136
136
 
137
- Saat membuat `ItemStack`, gunakan metode komponen `Component` alih-alih String lama:
137
+ When constructing `ItemStack` instances, use `Component` methods instead of legacy raw strings:
138
138
 
139
139
  ```java
140
140
  import org.bukkit.Material;
@@ -143,18 +143,14 @@ import org.bukkit.inventory.meta.ItemMeta;
143
143
  import java.util.List;
144
144
 
145
145
  ItemStack sword = new ItemStack(Material.DIAMOND_SWORD);
146
- ItemMeta meta = sword.getItemMeta();
147
-
148
- if (meta != null) {
149
- // Custom name menggunakan Component
150
- meta.displayName(MiniMessage.miniMessage().deserialize("<gradient:#00c6ff:#0072ff><bold>Pedang Es Legendaris</bold></gradient>"));
146
+ sword.editMeta(meta -> {
147
+ // Custom name using Component
148
+ meta.displayName(MiniMessage.miniMessage().deserialize("<gradient:#00c6ff:#0072ff><bold>Frostmourne Blade</bold></gradient>"));
151
149
 
152
- // Lore menggunakan List<Component>
150
+ // Lore using List<Component>
153
151
  meta.lore(List.of(
154
- MiniMessage.miniMessage().deserialize("<gray>Memberikan efek <aqua>Freeze</aqua> saat menyerang.</gray>"),
155
- MiniMessage.miniMessage().deserialize("<dark_gray>Level: <gold>★★★★★</gold></dark_gray>")
152
+ MiniMessage.miniMessage().deserialize("<gray>Applies <aqua>Freeze</aqua> on hit.</gray>"),
153
+ MiniMessage.miniMessage().deserialize("<dark_gray>Tier: <gold>★★★★★</gold></dark_gray>")
156
154
  ));
157
-
158
- sword.setItemMeta(meta);
159
- }
155
+ });
160
156
  ```
@@ -1,19 +1,19 @@
1
- # PersistentDataContainer (PDC) di Paper Minecraft
1
+ # PersistentDataContainer (PDC) in Modern Paper
2
2
 
3
- `PersistentDataContainer` (PDC) adalah API resmi Bukkit/Paper untuk menyimpan data kustom secara permanen pada objek dalam game seperti:
4
- - **Item (`ItemMeta`)**
5
- - **Entity (`Entity`, `LivingEntity`, `Player`)**
6
- - **Block State / Tile Entity (`TileState`)**
7
- - **Chunk (`Chunk`)**
8
- - **World (`World`)**
3
+ `PersistentDataContainer` (PDC) is the official, type-safe Bukkit/Paper API for storing custom persistent metadata directly on in-game Minecraft objects, including:
4
+ - **ItemStacks (`ItemMeta`)**
5
+ - **Entities (`Entity`, `LivingEntity`, `Player`)**
6
+ - **Block States / Tile Entities (`TileState`)**
7
+ - **Chunks (`Chunk`)**
8
+ - **Worlds (`World`)**
9
9
 
10
- PDC menggantikan ketergantungan pada library NBT pihak ketiga (seperti NBTAPI) atau pembacaan lore tersembunyi yang rapuh. Data yang disimpan di PDC disimpan langsung dalam NBT vanilla Minecraft dan otomatis disimpan/di-load bersama world data.
10
+ PDC eliminates brittle dependencies on legacy third-party NBT libraries (like NBTAPI) or parsing hidden lore strings. Data placed in PDC is serialized natively into Minecraft's vanilla NBT and automatically saved and loaded alongside world saves.
11
11
 
12
12
  ---
13
13
 
14
- ## 1. Konsep Kunci: NamespacedKey
14
+ ## 1. Key Concept: NamespacedKey
15
15
 
16
- Setiap nilai yang disimpan di PDC diidentifikasi oleh `NamespacedKey` unik yang terdiri dari `namespace` (biasanya nama plugin berhuruf kecil) dan `key`:
16
+ Every value stored in PDC is addressed by a unique `NamespacedKey` comprising a `namespace` (typically your lowercase plugin name) and a `key` identifier:
17
17
 
18
18
  ```java
19
19
  import org.bukkit.NamespacedKey;
@@ -34,40 +34,37 @@ public final class DataKeys {
34
34
 
35
35
  ---
36
36
 
37
- ## 2. Menyimpan & Membaca Tipe Data Primitif
37
+ ## 2. Storing & Reading Primitive Data Types
38
38
 
39
- PDC mendukung berbagai tipe bawaan melalui enum `PersistentDataType`:
39
+ PDC supports standard primitive types via the `PersistentDataType` interface:
40
40
  - `PersistentDataType.STRING`
41
41
  - `PersistentDataType.INTEGER`
42
42
  - `PersistentDataType.LONG`
43
43
  - `PersistentDataType.DOUBLE`
44
44
  - `PersistentDataType.FLOAT`
45
- - `PersistentDataType.BYTE` (boolean sering disimpan sebagai byte `1` / `0`)
45
+ - `PersistentDataType.BYTE` (booleans are stored as byte `1` / `0`)
46
46
  - `PersistentDataType.BYTE_ARRAY`
47
- - `PersistentDataType.TAG_CONTAINER` (PDC bersarang / nested)
47
+ - `PersistentDataType.TAG_CONTAINER` (nested PDC structures)
48
48
 
49
- ### Contoh Menyimpan Data pada ItemStack:
49
+ ### Example: Storing Metadata on an ItemStack
50
50
  ```java
51
51
  import org.bukkit.Material;
52
52
  import org.bukkit.inventory.ItemStack;
53
- import org.bukkit.inventory.meta.ItemMeta;
54
53
  import org.bukkit.persistence.PersistentDataContainer;
55
54
  import org.bukkit.persistence.PersistentDataType;
56
55
 
57
56
  public ItemStack createTeleportWand(String wandId) {
58
57
  ItemStack wand = new ItemStack(Material.BLAZE_ROD);
59
- ItemMeta meta = wand.getItemMeta();
60
- if (meta != null) {
58
+ wand.editMeta(meta -> {
61
59
  PersistentDataContainer pdc = meta.getPersistentDataContainer();
62
60
  pdc.set(DataKeys.CUSTOM_ID, PersistentDataType.STRING, wandId);
63
61
  pdc.set(DataKeys.COOLDOWN_TIMESTAMP, PersistentDataType.LONG, System.currentTimeMillis());
64
- wand.setItemMeta(meta);
65
- }
62
+ });
66
63
  return wand;
67
64
  }
68
65
  ```
69
66
 
70
- ### Contoh Membaca & Memvalidasi Data pada ItemStack:
67
+ ### Example: Reading & Validating ItemStack Metadata
71
68
  ```java
72
69
  public boolean isCustomWand(ItemStack item) {
73
70
  if (item == null || !item.hasItemMeta()) return false;
@@ -84,42 +81,45 @@ public String getWandId(ItemStack item) {
84
81
 
85
82
  ---
86
83
 
87
- ## 3. Menyimpan Data pada Entitas dan Chunk
84
+ ## 3. Storing Metadata on Entities and Chunks
88
85
 
89
- PDC juga dapat diakses langsung pada entitas dan chunk tanpa meta terpisah:
86
+ PDC is accessible directly on entities and chunks without wrapping metadata:
90
87
 
91
88
  ```java
92
89
  import org.bukkit.entity.Entity;
93
90
  import org.bukkit.Chunk;
91
+ import org.bukkit.NamespacedKey;
92
+ import org.bukkit.persistence.PersistentDataType;
94
93
 
95
- // Menandai entitas khusus (misal Custom Boss)
94
+ // Tagging custom entities (e.g. Dungeon Boss)
96
95
  public void markAsCustomBoss(Entity entity, String bossType) {
97
96
  entity.getPersistentDataContainer().set(DataKeys.CUSTOM_ID, PersistentDataType.STRING, bossType);
98
97
  }
99
98
 
100
- // Menandai chunk yang telah diproses oleh plugin
101
- public void markChunkProcessed(Chunk chunk) {
102
- chunk.getPersistentDataContainer().set(new NamespacedKey("myplugin", "processed"), PersistentDataType.BYTE, (byte) 1);
99
+ // Tagging chunks processed by your world generation systems
100
+ public void markChunkProcessed(Chunk chunk, NamespacedKey key) {
101
+ chunk.getPersistentDataContainer().set(key, PersistentDataType.BYTE, (byte) 1);
103
102
  }
104
103
  ```
105
104
 
106
105
  ---
107
106
 
108
- ## 4. Tipe Data Kustom (Complex Object Serialization)
107
+ ## 4. Custom Complex Data Types (`PersistentDataType<T, Z>`)
109
108
 
110
- Kita dapat mengimplementasikan antarmuka `PersistentDataType<T, Z>` kustom untuk menyimpan objek kompleks (seperti record/POJO) ke dalam PDC tanpa perlu serialisasi JSON manual di setiap panggilan.
109
+ You can implement custom `PersistentDataType<T, Z>` adapters to store complex Java objects (such as records or POJOs) directly into PDC without manual JSON conversions on every invocation.
111
110
 
112
- ### Contoh Record Objek:
111
+ ### Object Record:
113
112
  ```java
114
113
  public record ItemStats(int damageBonus, double critChance, int durability) {}
115
114
  ```
116
115
 
117
- ### Implementasi `PersistentDataType` Kustom:
116
+ ### Custom `PersistentDataType` Adapter:
118
117
  ```java
118
+ import org.bukkit.NamespacedKey;
119
119
  import org.bukkit.persistence.PersistentDataAdapterContext;
120
120
  import org.bukkit.persistence.PersistentDataContainer;
121
121
  import org.bukkit.persistence.PersistentDataType;
122
- import org.bukkit.NamespacedKey;
122
+ import org.bukkit.plugin.Plugin;
123
123
 
124
124
  public final class ItemStatsDataType implements PersistentDataType<PersistentDataContainer, ItemStats> {
125
125
 
@@ -127,7 +127,7 @@ public final class ItemStatsDataType implements PersistentDataType<PersistentDat
127
127
  private final NamespacedKey critKey;
128
128
  private final NamespacedKey durabilityKey;
129
129
 
130
- public ItemStatsDataType(org.bukkit.plugin.Plugin plugin) {
130
+ public ItemStatsDataType(Plugin plugin) {
131
131
  this.damageKey = new NamespacedKey(plugin, "damage");
132
132
  this.critKey = new NamespacedKey(plugin, "crit");
133
133
  this.durabilityKey = new NamespacedKey(plugin, "durability");
@@ -162,18 +162,18 @@ public final class ItemStatsDataType implements PersistentDataType<PersistentDat
162
162
  }
163
163
  ```
164
164
 
165
- ### Penggunaan Langsung:
165
+ ### Usage:
166
166
  ```java
167
167
  ItemStats stats = new ItemStats(25, 0.15, 500);
168
168
  pdc.set(DataKeys.ITEM_STATS, new ItemStatsDataType(plugin), stats);
169
169
 
170
- // Membaca kembali:
170
+ // Read back:
171
171
  ItemStats loadedStats = pdc.get(DataKeys.ITEM_STATS, new ItemStatsDataType(plugin));
172
172
  ```
173
173
 
174
174
  ---
175
175
 
176
- ## 5. Best Practices PDC
177
- 1. **Gunakan Singleton Keys**: Jangan instansiasi `new NamespacedKey(...)` berulang kali di hot-loop (seperti `EntityDamageByEntityEvent`); simpan sebagai static final constants.
178
- 2. **Namespace Konsisten**: Selalu gunakan plugin instance untuk key namespace agar tidak terjadi tabrakan dengan plugin lain.
179
- 3. **Pembersihan Data**: Hapus data yang tidak lagi valid menggunakan `pdc.remove(key)` untuk menghemat ukuran file world/NBT.
176
+ ## 5. PDC Best Practices
177
+ 1. **Singleton Keys**: Never instantiate `new NamespacedKey(...)` repeatedly in hot event loops; define static final constants.
178
+ 2. **Consistent Namespaces**: Always use your plugin instance namespace to prevent collisions with third-party plugins.
179
+ 3. **Data Cleanup**: Clean up stale metadata using `pdc.remove(key)` to minimize world save file sizes.