papermc-skill 1.1.0 → 1.2.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.
- package/.cursor/rules/paper-plugin-dev.mdc +23 -0
- package/.cursorrules +6 -0
- package/.paseo/rules.md +8 -0
- package/.pi/rules.md +8 -0
- package/.windsurfrules +7 -0
- package/CHANGELOG.md +31 -0
- package/CLAUDE.md +27 -0
- package/CODEX.md +12 -0
- package/DEEPSEEK.md +18 -0
- package/README.md +66 -26
- package/bin/cli.js +461 -36
- package/docs/12-data-components-and-display-entities.md +264 -0
- package/docs/13-multi-agent-system-and-tooling.md +90 -0
- package/package.json +16 -4
- package/skills/paper-plugin-dev/SKILL.md +144 -110
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/checksums.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/md5-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/sha1-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/kotlin-dsl-plugin-entries/kotlin-dsl-plugin-entries.lock +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/cache.properties +1 -1
- package/templates/paper-modern-template/build/tmp/compileJava/previous-compilation-data.bin +0 -0
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: paper-plugin-dev
|
|
3
|
-
description: Expert guidelines, modern architectural blueprints, exploit-prevention patterns, and best practices for developing production-grade Minecraft Java plugins using PaperMC API (1.20-1.21+ / 26.x), Folia (Threaded Regions), Adventure API, MiniMessage, and
|
|
3
|
+
description: Expert guidelines, modern architectural blueprints, exploit-prevention patterns, and best practices for developing production-grade Minecraft Java plugins using PaperMC API (1.20-1.21+ / 26.x), Folia (Threaded Regions), Adventure API, MiniMessage, Data Components, Display Entities, and Tricky Trials combat kinematics.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Paper & Folia Minecraft Plugin Development Skill
|
|
7
7
|
|
|
8
8
|
This skill provides authoritative architectural standards, high-level exploit-prevention blueprints, and modern design patterns for building and auditing Minecraft Java server plugins based on **PaperMC (1.20.6, 1.21+ through 26.x)** and **Folia (Regionized Multi-Threading)**.
|
|
9
9
|
|
|
10
|
+
Compatible with all premier AI coding environments: **Antigravity (agy)**, **Claude Code**, **OpenAI Codex**, **Cursor**, **DeepSeek Harness**, **Paseo**, and **pi.dev**.
|
|
11
|
+
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
## 1. Environment Prerequisites & Compatibility Matrix
|
|
13
15
|
|
|
14
16
|
| Component | Minimum Standard | Current Recommendation | Critical Notes |
|
|
15
17
|
| :--- | :--- | :--- | :--- |
|
|
16
|
-
| **Java SDK** | **Java 21 (LTS)** | **Java 21 LTS** / Java 25 Ready | Minecraft 1.20.5+ and modern Paper
|
|
17
|
-
| **Build Tool** | Gradle 8.10+ | **Gradle 9.
|
|
18
|
-
| **Paper API** | `1.21.4-R0.1-SNAPSHOT` | `1.21.11-R0.1-SNAPSHOT` | Repository: `https://repo.papermc.io/repository/maven-public/` |
|
|
18
|
+
| **Java SDK** | **Java 21 (LTS)** | **Java 21 LTS** / Java 25 Ready | Minecraft 1.20.5+ and modern Paper require Java 21+. Target Paper 26.x requires Java 25+. |
|
|
19
|
+
| **Build Tool** | Gradle 8.10+ | **Gradle 9.x+ (Kotlin DSL)** | Use `jvmToolchain(21)` and `pluginManagement { mavenCentral() }`. |
|
|
20
|
+
| **Paper API** | `1.21.4-R0.1-SNAPSHOT` | `1.21.11-R0.1-SNAPSHOT` / `26.3.build.+` | Repository: `https://repo.papermc.io/repository/maven-public/` |
|
|
19
21
|
| **API Discovery** | Manual | **PaperMC Fill v3 API** | Query real-time versions via `https://fill.papermc.io/v3/projects/paper`. |
|
|
20
22
|
| **Text Handling** | Adventure 4.17+ | **Kyori Adventure + MiniMessage** | Never use `org.bukkit.ChatColor` or the legacy section sign `§`. |
|
|
23
|
+
| **Item Data** | Data Components (1.20.5+) | `io.papermc.paper.datacomponent` | Legacy NBT tags are completely deprecated. Use `DataComponentTypes`. |
|
|
24
|
+
| **Visuals/FX** | ArmorStands (Legacy) | **Display Entities (Text/Item/Block)** | Zero-tick lag, JOML `Matrix4f` smooth client-side interpolation. |
|
|
21
25
|
|
|
22
26
|
---
|
|
23
27
|
|
|
@@ -35,13 +39,17 @@ When requested to scaffold, modify, or review a Paper/Folia plugin codebase:
|
|
|
35
39
|
- Block, World, or Chunk state: `RegionScheduler`.
|
|
36
40
|
- Global server state or announcements: `GlobalRegionScheduler`.
|
|
37
41
|
- Database, File I/O, Web Requests, or compute-heavy math: `AsyncScheduler` or `CompletableFuture`.
|
|
38
|
-
4. **
|
|
42
|
+
4. **Item & Data Component Modernization**:
|
|
43
|
+
- Never use raw NBT manipulation. Mutate items through `DataComponentTypes` (e.g. `FOOD`, `TOOL`, `CUSTOM_MODEL_DATA`, `MAX_STACK_SIZE`).
|
|
44
|
+
5. **Zero-Lag Visual Displays**:
|
|
45
|
+
- Replace outdated armor-stand floating holograms with `TextDisplay` and `ItemDisplay` using JOML matrix transformations.
|
|
46
|
+
6. **Enforce Exploit & Dupe Prevention Protocols (Mandatory)**:
|
|
39
47
|
- Validate numeric user input (reject `NaN`, `Infinity`, overflow, and negative amounts).
|
|
40
48
|
- Protect inventory menus against click-spam race conditions via debounce.
|
|
41
49
|
- Store `UUID` rather than `Player` instances across all caches and static collections.
|
|
42
50
|
- Secure custom items using `PersistentDataContainer` (PDC) signatures.
|
|
43
|
-
|
|
44
|
-
-
|
|
51
|
+
7. **Compile & Security Audit**:
|
|
52
|
+
- Run `npx papermc-skill audit` to guarantee 0 vulnerabilities before releasing code.
|
|
45
53
|
|
|
46
54
|
---
|
|
47
55
|
|
|
@@ -83,12 +91,12 @@ Invoking `world.getBlockAt(x, y, z)` on ungenerated/unloaded far chunks (e.g., c
|
|
|
83
91
|
|
|
84
92
|
---
|
|
85
93
|
|
|
86
|
-
## 4. Modern
|
|
94
|
+
## 4. Modern Overpowered (OP) Recipes
|
|
87
95
|
|
|
88
96
|
### Recipe 1: Folia-Ready Modern Manifest (`src/main/resources/paper-plugin.yml`)
|
|
89
97
|
```yaml
|
|
90
98
|
name: EpicPaperPlugin
|
|
91
|
-
version: '1.
|
|
99
|
+
version: '1.2.0'
|
|
92
100
|
main: com.example.plugin.EpicPlugin
|
|
93
101
|
bootstrapper: com.example.plugin.EpicPluginBootstrap
|
|
94
102
|
api-version: '1.21'
|
|
@@ -156,53 +164,143 @@ public class EpicPluginBootstrap implements PluginBootstrap {
|
|
|
156
164
|
}
|
|
157
165
|
```
|
|
158
166
|
|
|
159
|
-
### Recipe 3:
|
|
167
|
+
### Recipe 3: Modern Data Component Item Engineering (Zero-NBT)
|
|
160
168
|
```java
|
|
161
|
-
package com.example.plugin.
|
|
169
|
+
package com.example.plugin.item;
|
|
162
170
|
|
|
163
|
-
import
|
|
171
|
+
import io.papermc.paper.datacomponent.DataComponentTypes;
|
|
172
|
+
import io.papermc.paper.datacomponent.item.CustomModelData;
|
|
173
|
+
import io.papermc.paper.datacomponent.item.FoodProperties;
|
|
174
|
+
import io.papermc.paper.datacomponent.item.ItemLore;
|
|
175
|
+
import net.kyori.adventure.text.Component;
|
|
176
|
+
import net.kyori.adventure.text.format.NamedTextColor;
|
|
177
|
+
import net.kyori.adventure.text.format.TextDecoration;
|
|
164
178
|
import org.bukkit.Material;
|
|
165
|
-
import org.bukkit.NamespacedKey;
|
|
166
179
|
import org.bukkit.inventory.ItemStack;
|
|
167
|
-
import org.bukkit.persistence.PersistentDataContainer;
|
|
168
|
-
import org.bukkit.persistence.PersistentDataType;
|
|
169
|
-
import org.bukkit.plugin.Plugin;
|
|
170
180
|
|
|
171
|
-
import java.util.
|
|
181
|
+
import java.util.List;
|
|
172
182
|
|
|
173
|
-
public final class
|
|
183
|
+
public final class ModernItemBuilder {
|
|
174
184
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
private static NamespacedKey KEY_SIG;
|
|
185
|
+
public static ItemStack createGodtierRelic() {
|
|
186
|
+
ItemStack item = ItemStack.of(Material.NETHERITE_SWORD);
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
188
|
+
// Type-safe direct component modification
|
|
189
|
+
item.setData(DataComponentTypes.ITEM_NAME, Component.text("Aegis of the Void", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD));
|
|
190
|
+
item.setData(DataComponentTypes.LORE, ItemLore.lore(List.of(
|
|
191
|
+
Component.text("Ancient relic harvested from trial chambers.", NamedTextColor.GRAY),
|
|
192
|
+
Component.empty(),
|
|
193
|
+
Component.text("✦ Ability: Kinetic Shockwave on hit", NamedTextColor.AQUA)
|
|
194
|
+
)));
|
|
195
|
+
|
|
196
|
+
// Modern 1.20.5+ Custom Model Data (floats, strings, flags)
|
|
197
|
+
item.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
|
|
198
|
+
.addFloat(2.5f)
|
|
199
|
+
.addString("aegis_void_model")
|
|
200
|
+
.build()
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Overrides
|
|
204
|
+
item.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true);
|
|
205
|
+
item.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
206
|
+
item.setData(DataComponentTypes.UNBREAKABLE, true);
|
|
207
|
+
|
|
208
|
+
return item;
|
|
183
209
|
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
184
212
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
213
|
+
### Recipe 4: Zero-Lag Display Entity Hologram Engine
|
|
214
|
+
```java
|
|
215
|
+
package com.example.plugin.display;
|
|
216
|
+
|
|
217
|
+
import net.kyori.adventure.text.Component;
|
|
218
|
+
import org.bukkit.Color;
|
|
219
|
+
import org.bukkit.Location;
|
|
220
|
+
import org.bukkit.entity.Display;
|
|
221
|
+
import org.bukkit.entity.TextDisplay;
|
|
222
|
+
import org.bukkit.util.Transformation;
|
|
223
|
+
import org.joml.AxisAngle4f;
|
|
224
|
+
import org.joml.Vector3f;
|
|
225
|
+
|
|
226
|
+
public final class DisplayHologramEngine {
|
|
227
|
+
|
|
228
|
+
/** Spawns a floating holographic sign with smooth client-side interpolation */
|
|
229
|
+
public static TextDisplay spawnSmoothHologram(Location loc, Component text) {
|
|
230
|
+
return loc.getWorld().spawn(loc, TextDisplay.class, display -> {
|
|
231
|
+
display.text(text);
|
|
232
|
+
display.setBillboard(Display.Billboard.CENTER);
|
|
233
|
+
display.setDefaultBackground(false);
|
|
234
|
+
display.setBackgroundColor(Color.fromARGB(150, 20, 20, 30));
|
|
235
|
+
display.setShadowed(true);
|
|
236
|
+
|
|
237
|
+
// Client-side smooth interpolation (20 ticks = 1 second)
|
|
238
|
+
display.setInterpolationDuration(20);
|
|
239
|
+
display.setInterpolationDelay(0);
|
|
240
|
+
|
|
241
|
+
// Transformation via JOML
|
|
242
|
+
Transformation transform = new Transformation(
|
|
243
|
+
new Vector3f(0f, 0f, 0f),
|
|
244
|
+
new AxisAngle4f(0f, 0f, 1f, 0f),
|
|
245
|
+
new Vector3f(1.1f, 1.1f, 1.1f),
|
|
246
|
+
new AxisAngle4f(0f, 0f, 1f, 0f)
|
|
247
|
+
);
|
|
248
|
+
display.setTransformation(transform);
|
|
193
249
|
});
|
|
194
|
-
return item;
|
|
195
250
|
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Recipe 5: 1.21 Tricky Trials Mace & Combat Physics
|
|
255
|
+
```java
|
|
256
|
+
package com.example.plugin.combat;
|
|
257
|
+
|
|
258
|
+
import net.kyori.adventure.text.Component;
|
|
259
|
+
import net.kyori.adventure.text.format.NamedTextColor;
|
|
260
|
+
import org.bukkit.Location;
|
|
261
|
+
import org.bukkit.Material;
|
|
262
|
+
import org.bukkit.Particle;
|
|
263
|
+
import org.bukkit.Sound;
|
|
264
|
+
import org.bukkit.entity.LivingEntity;
|
|
265
|
+
import org.bukkit.entity.Player;
|
|
266
|
+
import org.bukkit.event.EventHandler;
|
|
267
|
+
import org.bukkit.event.EventPriority;
|
|
268
|
+
import org.bukkit.event.Listener;
|
|
269
|
+
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
270
|
+
import org.bukkit.util.Vector;
|
|
271
|
+
|
|
272
|
+
public final class MaceKineticCombatListener implements Listener {
|
|
273
|
+
|
|
274
|
+
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
|
275
|
+
public void onMaceSmash(EntityDamageByEntityEvent event) {
|
|
276
|
+
if (!(event.getDamager() instanceof Player attacker)) return;
|
|
277
|
+
if (!(event.getEntity() instanceof LivingEntity victim)) return;
|
|
196
278
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
279
|
+
if (attacker.getInventory().getItemInMainHand().getType() != Material.MACE) return;
|
|
280
|
+
|
|
281
|
+
float fallDistance = attacker.getFallDistance();
|
|
282
|
+
if (fallDistance > 1.5f) {
|
|
283
|
+
double extraDmg = Math.min(fallDistance * 3.5, 45.0);
|
|
284
|
+
event.setDamage(event.getDamage() + extraDmg);
|
|
285
|
+
|
|
286
|
+
Location loc = victim.getLocation();
|
|
287
|
+
loc.getWorld().spawnParticle(Particle.TRIAL_SPAWNER_DETECTION, loc.add(0, 0.5, 0), 30, 0.5, 0.2, 0.5, 0.05);
|
|
288
|
+
loc.getWorld().playSound(loc, Sound.ITEM_MACE_SMASH_GROUND, 1.2f, 1.0f);
|
|
289
|
+
|
|
290
|
+
// Shockwave knockback
|
|
291
|
+
loc.getWorld().getNearbyLivingEntities(loc, 4.5, e -> !e.equals(attacker) && !e.equals(victim))
|
|
292
|
+
.forEach(e -> {
|
|
293
|
+
Vector impulse = e.getLocation().toVector().subtract(loc.toVector()).normalize().multiply(1.3).setY(0.4);
|
|
294
|
+
e.setVelocity(impulse);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
attacker.sendActionBar(Component.text("⚡ SMASH IMPACT: +" + String.format("%.1f", extraDmg) + " DMG", NamedTextColor.GOLD));
|
|
298
|
+
}
|
|
201
299
|
}
|
|
202
300
|
}
|
|
203
301
|
```
|
|
204
302
|
|
|
205
|
-
### Recipe
|
|
303
|
+
### Recipe 6: Anti-Dupe GUI Controller with Debounce
|
|
206
304
|
```java
|
|
207
305
|
package com.example.plugin.gui;
|
|
208
306
|
|
|
@@ -278,14 +376,12 @@ public class SafeMenuController implements InventoryHolder, Listener {
|
|
|
278
376
|
}
|
|
279
377
|
|
|
280
378
|
private void handleSlotAction(Player player, int slot) {
|
|
281
|
-
//
|
|
379
|
+
// Safe slot logic
|
|
282
380
|
}
|
|
283
381
|
}
|
|
284
382
|
```
|
|
285
383
|
|
|
286
|
-
### Recipe
|
|
287
|
-
Transparently dispatches execution safely whether running on standard Paper or multi-threaded Folia:
|
|
288
|
-
|
|
384
|
+
### Recipe 7: Universal Folia & Paper Scheduler Adapter
|
|
289
385
|
```java
|
|
290
386
|
package com.example.plugin.util;
|
|
291
387
|
|
|
@@ -315,7 +411,6 @@ public final class UniversalScheduler {
|
|
|
315
411
|
return IS_FOLIA;
|
|
316
412
|
}
|
|
317
413
|
|
|
318
|
-
/** Dispatches a task to the region scheduler owning the specified entity */
|
|
319
414
|
public static void runForEntity(Plugin plugin, Entity entity, Runnable runnable) {
|
|
320
415
|
if (IS_FOLIA) {
|
|
321
416
|
entity.getScheduler().run(plugin, task -> runnable.run(), null);
|
|
@@ -324,7 +419,6 @@ public final class UniversalScheduler {
|
|
|
324
419
|
}
|
|
325
420
|
}
|
|
326
421
|
|
|
327
|
-
/** Dispatches a task to the region scheduler owning the specified location */
|
|
328
422
|
public static void runAtLocation(Plugin plugin, Location location, Runnable runnable) {
|
|
329
423
|
if (IS_FOLIA) {
|
|
330
424
|
Bukkit.getRegionScheduler().execute(plugin, location, runnable);
|
|
@@ -333,7 +427,6 @@ public final class UniversalScheduler {
|
|
|
333
427
|
}
|
|
334
428
|
}
|
|
335
429
|
|
|
336
|
-
/** Dispatches a purely asynchronous task (I/O, database, networking) */
|
|
337
430
|
public static void runAsync(Plugin plugin, Runnable runnable) {
|
|
338
431
|
if (IS_FOLIA) {
|
|
339
432
|
Bukkit.getAsyncScheduler().runNow(plugin, task -> runnable.run());
|
|
@@ -342,7 +435,6 @@ public final class UniversalScheduler {
|
|
|
342
435
|
}
|
|
343
436
|
}
|
|
344
437
|
|
|
345
|
-
/** Dispatches a delayed asynchronous task */
|
|
346
438
|
public static void runAsyncDelayed(Plugin plugin, Runnable runnable, long delayMs) {
|
|
347
439
|
if (IS_FOLIA) {
|
|
348
440
|
Bukkit.getAsyncScheduler().runDelayed(plugin, task -> runnable.run(), delayMs, TimeUnit.MILLISECONDS);
|
|
@@ -354,66 +446,6 @@ public final class UniversalScheduler {
|
|
|
354
446
|
}
|
|
355
447
|
```
|
|
356
448
|
|
|
357
|
-
### Recipe 6: Robust Asynchronous Database Access (HikariCP)
|
|
358
|
-
```java
|
|
359
|
-
package com.example.plugin.database;
|
|
360
|
-
|
|
361
|
-
import com.zaxxer.hikari.HikariConfig;
|
|
362
|
-
import com.zaxxer.hikari.HikariDataSource;
|
|
363
|
-
|
|
364
|
-
import java.sql.Connection;
|
|
365
|
-
import java.sql.PreparedStatement;
|
|
366
|
-
import java.sql.ResultSet;
|
|
367
|
-
import java.sql.SQLException;
|
|
368
|
-
import java.util.UUID;
|
|
369
|
-
import java.util.concurrent.CompletableFuture;
|
|
370
|
-
import java.util.concurrent.ExecutorService;
|
|
371
|
-
import java.util.concurrent.Executors;
|
|
372
|
-
|
|
373
|
-
public class DatabaseManager {
|
|
374
|
-
|
|
375
|
-
private final HikariDataSource dataSource;
|
|
376
|
-
private final ExecutorService dbExecutor = Executors.newFixedThreadPool(4);
|
|
377
|
-
|
|
378
|
-
public DatabaseManager(String jdbcUrl, String username, String password) {
|
|
379
|
-
HikariConfig config = new HikariConfig();
|
|
380
|
-
config.setJdbcUrl(jdbcUrl);
|
|
381
|
-
config.setUsername(username);
|
|
382
|
-
config.setPassword(password);
|
|
383
|
-
config.setMaximumPoolSize(10);
|
|
384
|
-
config.setMinimumIdle(2);
|
|
385
|
-
config.setConnectionTimeout(5000);
|
|
386
|
-
config.setPoolName("PaperPlugin-HikariPool");
|
|
387
|
-
this.dataSource = new HikariDataSource(config);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
public CompletableFuture<Integer> getCoinsAsync(UUID playerUuid) {
|
|
391
|
-
return CompletableFuture.supplyAsync(() -> {
|
|
392
|
-
String query = "SELECT coins FROM player_data WHERE uuid = ?";
|
|
393
|
-
try (Connection conn = dataSource.getConnection();
|
|
394
|
-
PreparedStatement stmt = conn.prepareStatement(query)) {
|
|
395
|
-
stmt.setString(1, playerUuid.toString());
|
|
396
|
-
try (ResultSet rs = stmt.executeQuery()) {
|
|
397
|
-
if (rs.next()) {
|
|
398
|
-
return rs.getInt("coins");
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
} catch (SQLException e) {
|
|
402
|
-
e.printStackTrace();
|
|
403
|
-
}
|
|
404
|
-
return 0;
|
|
405
|
-
}, dbExecutor);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
public void shutdown() {
|
|
409
|
-
dbExecutor.shutdown();
|
|
410
|
-
if (dataSource != null && !dataSource.isClosed()) {
|
|
411
|
-
dataSource.close();
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
449
|
---
|
|
418
450
|
|
|
419
451
|
## 5. Anti-Patterns vs Modern Standards
|
|
@@ -422,12 +454,12 @@ public class DatabaseManager {
|
|
|
422
454
|
| :--- | :--- | :--- |
|
|
423
455
|
| `ChatColor.RED + "Message"` | Deprecated & breaks console/client formatting | `MiniMessage.miniMessage().deserialize("<red>Message</red>")` |
|
|
424
456
|
| `public static Set<Player> list;` | Causes massive **Memory Leaks** of worlds & connections | `public static Set<UUID> list;` |
|
|
457
|
+
| Direct NBT manipulation (`net.minecraft.nbt`) | Hard crashes on 1.20.5+ / 1.21+ / 26.x | `ItemStack#setData(DataComponentTypes.XXX)` |
|
|
458
|
+
| `ArmorStand` floating text holograms | Extreme entity ticking CPU overhead | `TextDisplay` with JOML interpolated transformations |
|
|
425
459
|
| `world.getChunkAt(x, z)` in a loop | Freezes main server tick thread (major TPS drops) | `world.getChunkAtAsync(x, z)` |
|
|
426
460
|
| `Bukkit.getScheduler()` on Folia | Throws exception or causes thread-safety desync | Use `RegionScheduler`, `EntityScheduler`, or `UniversalScheduler` |
|
|
427
461
|
| Modifying inventories asynchronously | Causes item duplication and memory corruption | Always dispatch inventory modifications to entity region thread |
|
|
428
462
|
| Concatenating raw SQL queries | High vulnerability to **SQL Injection** | Always use `PreparedStatement` with `?` parameters |
|
|
429
|
-
| `new NamespacedKey(...)` in event loops | Excessive GC allocation pressure | Define constants as `public static final NamespacedKey` |
|
|
430
|
-
| Relying on item displayName/lore | Easily forged via anvils or modified clients | Always verify identity and attributes via **PDC** |
|
|
431
463
|
| Unbounded numbers in economy commands | Susceptible to `NaN`, negative amounts, and overflow | Validate `Double.isFinite()` and enforce minimum/maximum boundaries |
|
|
432
464
|
|
|
433
465
|
---
|
|
@@ -438,10 +470,12 @@ Prior to releasing a plugin to production or public servers:
|
|
|
438
470
|
|
|
439
471
|
- [ ] `paper-plugin.yml` is defined with `folia-supported: true`.
|
|
440
472
|
- [ ] Zero references to `org.bukkit.ChatColor` across the codebase.
|
|
473
|
+
- [ ] Direct NBT or legacy ItemMeta serialization replaced by `DataComponentTypes`.
|
|
474
|
+
- [ ] Holograms and floating 3D icons use `Display` entities rather than armor stands.
|
|
441
475
|
- [ ] All command registration uses modern Brigadier (`LifecycleEvents.COMMANDS`).
|
|
442
476
|
- [ ] No direct storage of `Player`, `World`, or `Entity` instances in static collections.
|
|
443
477
|
- [ ] All File, Database, and HTTP operations execute 100% asynchronously.
|
|
444
478
|
- [ ] Custom GUIs implement `setCancelled(true)` and click debounce cooldowns.
|
|
445
479
|
- [ ] Custom items are identified and verified via `PersistentDataContainer` (PDC).
|
|
446
480
|
- [ ] All database queries use connection pooling enclosed in `try-with-resources`.
|
|
447
|
-
- [ ] Plugin
|
|
481
|
+
- [ ] Plugin passes `npx papermc-skill audit` with zero issues.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.lock
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#
|
|
1
|
+
#Wed Nov 11 04:17:31 UTC 2026
|
|
2
2
|
gradle.version=9.7.1
|