minecraft-plugin-dev — for Claude Code minecraft-plugin-dev, minecraft-agent-skills, community, for Claude Code, ide skills, agent-skills, ai-agents, claude-code, fabric, minecraft

v1.0.0

关于此技能

适用场景: Ideal for AI agents that need minecraft plugin development skill. 本地化技能摘要: Minecraft AI agent skills and dual-target plugin bundle for Codex and Claude Code. It covers agent-skills, ai-agents, claude-code workflows.

功能特性

Minecraft Plugin Development Skill
Platform Base API Notes
---------- ---------- -------
Paper Bukkit/Spigot + Paper extensions Recommended; async chunk loading, Adventure native
Spigot Bukkit + Spigot extensions Legacy; fewer APIs, slower

# 核心主题

Jahrome907 Jahrome907
[11]
[2]
更新于: 4/21/2026

Killer-Skills Review

Decision support comes first. Repository text comes second.

Reference-Only Page Review Score: 10/11

This page remains useful for operators, but Killer-Skills treats it as reference material instead of a primary organic landing page.

Original recommendation layer Concrete use-case guidance Explicit limitations and caution Quality floor passed for review
Review Score
10/11
Quality Score
70
Canonical Locale
en
Detected Body Locale
en

适用场景: Ideal for AI agents that need minecraft plugin development skill. 本地化技能摘要: Minecraft AI agent skills and dual-target plugin bundle for Codex and Claude Code. It covers agent-skills, ai-agents, claude-code workflows.

核心价值

推荐说明: minecraft-plugin-dev helps agents minecraft plugin development skill. Minecraft AI agent skills and dual-target plugin bundle for Codex and Claude Code.

适用 Agent 类型

适用场景: Ideal for AI agents that need minecraft plugin development skill.

赋予的主要能力 · minecraft-plugin-dev

适用任务: Applying Minecraft Plugin Development Skill
适用任务: Applying Platform Base API Notes
适用任务: Applying ---------- ---------- -------

! 使用限制与门槛

  • 限制说明: Bukkit Base API only Avoid for new plugins
  • 限制说明: Folia Paper fork Region-threaded; requires special scheduler APIs
  • 限制说明: Do not use when: the task is pure vanilla datapack/command content (minecraft-datapack / minecraft-commands-scripting).

Why this page is reference-only

  • - Current locale does not satisfy the locale-governance contract.

Source Boundary

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

评审后的下一步

先决定动作,再继续看上游仓库材料

Killer-Skills 的主价值不应该停在“帮你打开仓库说明”,而是先帮你判断这项技能是否值得安装、是否应该回到可信集合复核,以及是否已经进入工作流落地阶段。

实验室 Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox

常见问题与安装步骤

以下问题与步骤与页面结构化数据保持一致,便于搜索引擎理解页面内容。

? FAQ

minecraft-plugin-dev 是什么?

适用场景: Ideal for AI agents that need minecraft plugin development skill. 本地化技能摘要: Minecraft AI agent skills and dual-target plugin bundle for Codex and Claude Code. It covers agent-skills, ai-agents, claude-code workflows.

如何安装 minecraft-plugin-dev?

运行命令:npx killer-skills add Jahrome907/minecraft-agent-skills/minecraft-plugin-dev。支持 Cursor、Windsurf、VS Code、Claude Code 等 19+ IDE/Agent。

minecraft-plugin-dev 适用于哪些场景?

典型场景包括:适用任务: Applying Minecraft Plugin Development Skill、适用任务: Applying Platform Base API Notes、适用任务: Applying ---------- ---------- -------。

minecraft-plugin-dev 支持哪些 IDE 或 Agent?

该技能兼容 Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer。可使用 Killer-Skills CLI 一条命令通用安装。

minecraft-plugin-dev 有哪些限制?

限制说明: Bukkit Base API only Avoid for new plugins;限制说明: Folia Paper fork Region-threaded; requires special scheduler APIs;限制说明: Do not use when: the task is pure vanilla datapack/command content (minecraft-datapack / minecraft-commands-scripting).。

安装步骤

  1. 1. 打开终端

    在你的项目目录中打开终端或命令行。

  2. 2. 执行安装命令

    运行:npx killer-skills add Jahrome907/minecraft-agent-skills/minecraft-plugin-dev。CLI 会自动识别 IDE 或 AI Agent 并完成配置。

  3. 3. 开始使用技能

    minecraft-plugin-dev 已启用,可立即在当前项目中调用。

! 参考页模式

此页面仍可作为安装与查阅参考,但 Killer-Skills 不再把它视为主要可索引落地页。请优先阅读上方评审结论,再决定是否继续查看上游仓库说明。

Upstream Repository Material

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

Upstream Source

minecraft-plugin-dev

Minecraft AI agent skills and dual-target plugin bundle for Codex and Claude Code. It covers agent-skills, ai-agents, claude-code workflows. Minecraft Plugin

SKILL.md
Readonly
Upstream Repository Material
The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.
Supporting Evidence

Minecraft Plugin Development Skill

Platform Overview

PlatformBase APINotes
PaperBukkit/Spigot + Paper extensionsRecommended; async chunk loading, Adventure native
SpigotBukkit + Spigot extensionsLegacy; fewer APIs, slower
BukkitBase API onlyAvoid for new plugins
FoliaPaper forkRegion-threaded; requires special scheduler APIs

Paper is the recommended target. Paper includes all Bukkit and Spigot APIs plus significant performance improvements and additional APIs.

Routing Boundaries

  • Use when: the target is server-side Paper/Bukkit/Spigot plugin behavior with JavaPlugin APIs.
  • Do not use when: the task requires client-side installable mods or loader APIs (minecraft-modding / minecraft-multiloader).
  • Do not use when: the task is pure vanilla datapack/command content (minecraft-datapack / minecraft-commands-scripting).

Project Setup

settings.gradle.kts

kotlin
1rootProject.name = "my-plugin"

build.gradle.kts

kotlin
1plugins { 2 java 3 id("com.gradleup.shadow") version "8.3.0" 4} 5 6group = "com.example" 7version = "1.0.0-SNAPSHOT" 8 9repositories { 10 mavenCentral() 11 maven("https://repo.papermc.io/repository/maven-public/") 12 // For Vault (economy API) 13 maven("https://jitpack.io") 14} 15 16dependencies { 17 compileOnly("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT") 18 // Optional: Vault economy/permission integration 19 compileOnly("com.github.MilkBowl:VaultAPI:1.7") 20} 21 22java { 23 toolchain.languageVersion.set(JavaLanguageVersion.of(21)) 24} 25 26tasks { 27 processResources { 28 // Substitutes ${version} in plugin.yml with the Gradle project version 29 filesMatching(listOf("plugin.yml", "paper-plugin.yml")) { 30 expand("version" to project.version) 31 } 32 } 33 shadowJar { 34 archiveClassifier.set("") 35 } 36 build { 37 dependsOn(shadowJar) 38 } 39}

gradle/wrapper/gradle-wrapper.properties

properties
1distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip

Project Layout

my-plugin/
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/
│   └── wrapper/
│       └── gradle-wrapper.properties
└── src/main/
    ├── java/com/example/myplugin/
    │   ├── MyPlugin.java          ← main class (extends JavaPlugin)
    │   ├── listeners/
    │   │   └── PlayerListener.java
    │   ├── commands/
    │   │   └── MyCommand.java
    │   └── managers/
    │       └── DataManager.java
    └── resources/
        ├── plugin.yml
        ├── paper-plugin.yml      ← optional, Paper-only metadata
        └── config.yml

Core Files

plugin.yml (Bukkit-compatible default)

yaml
1name: MyPlugin 2version: "${version}" 3main: com.example.myplugin.MyPlugin 4description: An example Paper plugin 5author: YourName 6website: https://github.com/example/my-plugin 7api-version: '1.21.11' 8 9commands: 10 myplugin: 11 description: Main plugin command 12 usage: /myplugin <subcommand> 13 permission: myplugin.use 14 aliases: [mp] 15 16permissions: 17 myplugin.use: 18 description: Allows use of /myplugin 19 default: true 20 myplugin.admin: 21 description: Admin access 22 default: op

Paper 1.20.5+ supports major/minor/patch api-version values. Use api-version: '1.21.11' when you target that Paper patch specifically, or api-version: '1.21' only when you intentionally support the broader 1.21.x line. In this repo, the validator accepts 1.21 plus positive 1.21.<patch> values on the 1.21 line. Patches newer than the repo's current example patch (1.21.11) are allowed but warned so future Paper updates do not force an immediate validator edit. Values such as 1.21.0, 1.21.01, or 1.22 are rejected.

paper-plugin.yml (Paper-only metadata)

Use paper-plugin.yml when you need Paper-specific metadata such as folia-supported or server/bootstrap dependency ordering. Keep plugin.yml if you must stay portable to Bukkit-derived servers that do not understand the Paper-specific file.

yaml
1name: MyPlugin 2version: "${version}" 3main: com.example.myplugin.MyPlugin 4api-version: '1.21.11' 5folia-supported: true 6 7dependencies: 8 server: 9 Vault: 10 load: BEFORE 11 required: false

Main Plugin Class

java
1package com.example.myplugin; 2 3import com.example.myplugin.commands.MyCommand; 4import com.example.myplugin.listeners.PlayerListener; 5import org.bukkit.plugin.java.JavaPlugin; 6 7public final class MyPlugin extends JavaPlugin { 8 9 private static MyPlugin instance; 10 11 @Override 12 public void onEnable() { 13 instance = this; 14 saveDefaultConfig(); 15 16 // Register listeners 17 getServer().getPluginManager().registerEvents(new PlayerListener(this), this); 18 19 // Register commands 20 var cmd = getCommand("myplugin"); 21 if (cmd != null) { 22 cmd.setExecutor(new MyCommand(this)); 23 cmd.setTabCompleter(new MyCommand(this)); 24 } 25 26 getLogger().info("MyPlugin enabled!"); 27 } 28 29 @Override 30 public void onDisable() { 31 getLogger().info("MyPlugin disabled."); 32 } 33 34 public static MyPlugin getInstance() { 35 return instance; 36 } 37}

Event Listeners

java
1package com.example.myplugin.listeners; 2 3import com.example.myplugin.MyPlugin; 4import net.kyori.adventure.text.Component; 5import net.kyori.adventure.text.format.NamedTextColor; 6import org.bukkit.event.EventHandler; 7import org.bukkit.event.EventPriority; 8import org.bukkit.event.Listener; 9import org.bukkit.event.entity.PlayerDeathEvent; 10import org.bukkit.event.player.PlayerJoinEvent; 11import org.bukkit.event.player.PlayerQuitEvent; 12 13public class PlayerListener implements Listener { 14 15 private final MyPlugin plugin; 16 17 public PlayerListener(MyPlugin plugin) { 18 this.plugin = plugin; 19 } 20 21 @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) 22 public void onPlayerJoin(PlayerJoinEvent event) { 23 event.joinMessage( 24 Component.text(event.getPlayer().getName() + " joined!", NamedTextColor.GREEN) 25 ); 26 } 27 28 @EventHandler 29 public void onPlayerQuit(PlayerQuitEvent event) { 30 event.quitMessage( 31 Component.text(event.getPlayer().getName() + " left.", NamedTextColor.YELLOW) 32 ); 33 } 34 35 @EventHandler(ignoreCancelled = true) 36 public void onPlayerDeath(PlayerDeathEvent event) { 37 // Modify death message using Adventure components 38 event.deathMessage( 39 Component.text("☠ ", NamedTextColor.RED) 40 .append(Component.text(event.getPlayer().getName(), NamedTextColor.WHITE)) 41 .append(Component.text(" died!", NamedTextColor.RED)) 42 ); 43 } 44}

EventPriority order

LOWEST → LOW → NORMAL → HIGH → HIGHEST → MONITOR
Use MONITOR for logging only (never modify outcome). Use ignoreCancelled = true unless you have a specific reason to handle cancelled events.

Cancellable events

java
1@EventHandler 2public void onBlockBreak(BlockBreakEvent event) { 3 if (event.getPlayer().hasPermission("myplugin.break.deny")) { 4 event.setCancelled(true); 5 event.getPlayer().sendMessage(Component.text("You cannot break blocks!", NamedTextColor.RED)); 6 } 7}

Commands

java
1package com.example.myplugin.commands; 2 3import com.example.myplugin.MyPlugin; 4import net.kyori.adventure.text.Component; 5import net.kyori.adventure.text.format.NamedTextColor; 6import org.bukkit.command.Command; 7import org.bukkit.command.CommandExecutor; 8import org.bukkit.command.CommandSender; 9import org.bukkit.command.TabCompleter; 10import org.bukkit.entity.Player; 11import org.jetbrains.annotations.NotNull; 12import org.jetbrains.annotations.Nullable; 13 14import java.util.List; 15 16public class MyCommand implements CommandExecutor, TabCompleter { 17 18 private final MyPlugin plugin; 19 20 public MyCommand(MyPlugin plugin) { 21 this.plugin = plugin; 22 } 23 24 @Override 25 public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, 26 @NotNull String label, @NotNull String[] args) { 27 if (!(sender instanceof Player player)) { 28 sender.sendMessage(Component.text("Only players can use this command.", NamedTextColor.RED)); 29 return true; 30 } 31 32 if (!player.hasPermission("myplugin.use")) { 33 player.sendMessage(Component.text("No permission.", NamedTextColor.RED)); 34 return true; 35 } 36 37 if (args.length == 0) { 38 player.sendMessage(Component.text("Usage: /myplugin <reload|info>", NamedTextColor.YELLOW)); 39 return true; 40 } 41 42 return switch (args[0].toLowerCase()) { 43 case "reload" -> { 44 plugin.reloadConfig(); 45 player.sendMessage(Component.text("Config reloaded.", NamedTextColor.GREEN)); 46 yield true; 47 } 48 case "info" -> { 49 player.sendMessage(Component.text("Version: " + plugin.getDescription().getVersion(), NamedTextColor.AQUA)); 50 yield true; 51 } 52 default -> { 53 player.sendMessage(Component.text("Unknown subcommand.", NamedTextColor.RED)); 54 yield false; 55 } 56 }; 57 } 58 59 @Override 60 public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, 61 @NotNull String label, @NotNull String[] args) { 62 if (args.length == 1) { 63 return List.of("reload", "info").stream() 64 .filter(s -> s.startsWith(args[0].toLowerCase())) 65 .toList(); 66 } 67 return List.of(); 68 } 69}

Schedulers

For classic Paper plugins, BukkitScheduler is still fine. If you claim Folia support, move entity, region, and global work onto the Folia-aware schedulers instead of assuming one global main thread.

Synchronous (runs on main thread)

java
1// Run once after 20 ticks (1 second) 2plugin.getServer().getScheduler().runTaskLater(plugin, () -> { 3 // safe to access Bukkit API here 4}, 20L); 5 6// Repeating task every 40 ticks (2 seconds), starts after 0 ticks 7plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { 8 // runs on main thread 9}, 0L, 40L);

Asynchronous (for I/O / database work)

java
1// Never touch Bukkit API in async tasks! 2plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { 3 // safe: file I/O, HTTP requests, DB queries 4 String data = fetchFromDatabase(); 5 // Switch back to main thread to use Bukkit API 6 plugin.getServer().getScheduler().runTask(plugin, () -> { 7 Bukkit.broadcastMessage(data); 8 }); 9});

BukkitRunnable (cancelable tasks)

java
1new BukkitRunnable() { 2 int count = 0; 3 4 @Override 5 public void run() { 6 count++; 7 if (count >= 10) { 8 cancel(); // stop after 10 executions 9 return; 10 } 11 // task logic 12 } 13}.runTaskTimer(plugin, 0L, 20L);

Folia-safe scheduling

java
1// Player-bound work: stays with the player's owning region 2player.getScheduler().run(plugin, task -> { 3 player.sendActionBar(Component.text("Checkpoint reached")); 4}, null); 5 6// Location / chunk-bound work 7plugin.getServer().getRegionScheduler().run(plugin, location, task -> { 8 location.getBlock().setType(Material.GOLD_BLOCK); 9}); 10 11// Global coordination that is not tied to one region 12plugin.getServer().getGlobalRegionScheduler().run(plugin, task -> { 13 Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "save-all"); 14}); 15 16// Async I/O remains on the async scheduler 17plugin.getServer().getAsyncScheduler().runNow(plugin, task -> { 18 writeAuditLog(); 19});

If you need to support both Paper and Folia, hide scheduling behind your own interface instead of scattering scheduler calls throughout listeners and commands.


Persistent Data Container (PDC)

PDC stores arbitrary data on any PersistentDataHolder (players, entities, items, chunks). Data is saved with the world and persists across restarts.

java
1import org.bukkit.NamespacedKey; 2import org.bukkit.persistence.PersistentDataType; 3 4// Define keys (reuse instances — create once in your plugin class) 5NamespacedKey killKey = new NamespacedKey(plugin, "kill_count"); 6NamespacedKey flagKey = new NamespacedKey(plugin, "vip"); 7 8// Write 9player.getPersistentDataContainer().set(killKey, PersistentDataType.INTEGER, 42); 10player.getPersistentDataContainer().set(flagKey, PersistentDataType.BOOLEAN, true); 11 12// Read 13int kills = player.getPersistentDataContainer() 14 .getOrDefault(killKey, PersistentDataType.INTEGER, 0); 15 16boolean isVip = player.getPersistentDataContainer() 17 .getOrDefault(flagKey, PersistentDataType.BOOLEAN, false); 18 19// Check existence 20boolean hasData = player.getPersistentDataContainer().has(killKey, PersistentDataType.INTEGER); 21 22// Remove 23player.getPersistentDataContainer().remove(killKey);

PDC on ItemStack

java
1ItemStack item = new ItemStack(Material.DIAMOND_SWORD); 2item.editMeta(meta -> meta.getPersistentDataContainer().set( 3 new NamespacedKey(plugin, "custom_id"), 4 PersistentDataType.STRING, 5 "special_sword" 6));

PDC on chunks or worlds

java
1NamespacedKey arenaKey = new NamespacedKey(plugin, "arena_id"); 2 3chunk.getPersistentDataContainer().set(arenaKey, PersistentDataType.STRING, "spawn"); 4 5String arenaId = chunk.getPersistentDataContainer() 6 .getOrDefault(arenaKey, PersistentDataType.STRING, "unknown");

Adventure Text Components

Paper uses Adventure natively for all text. No legacy chat colors.

java
1import net.kyori.adventure.text.Component; 2import net.kyori.adventure.text.format.NamedTextColor; 3import net.kyori.adventure.text.format.TextDecoration; 4import net.kyori.adventure.text.event.ClickEvent; 5import net.kyori.adventure.text.event.HoverEvent; 6 7// Simple components 8player.sendMessage(Component.text("Hello!", NamedTextColor.GREEN)); 9player.sendMessage(Component.text("Bold warning", NamedTextColor.RED, TextDecoration.BOLD)); 10 11// Compound component 12Component message = Component.text() 13 .append(Component.text("[Click Me]", NamedTextColor.AQUA) 14 .clickEvent(ClickEvent.runCommand("/myplugin info")) 15 .hoverEvent(HoverEvent.showText(Component.text("Run /myplugin info")))) 16 .append(Component.text(" to see plugin info.", NamedTextColor.WHITE)) 17 .build(); 18player.sendMessage(message); 19 20// MiniMessage (recommended for config-driven text) 21import net.kyori.adventure.text.minimessage.MiniMessage; 22Component parsed = MiniMessage.miniMessage().deserialize( 23 "<gradient:red:yellow>Hello World</gradient>" 24); 25 26// Titles / action bars 27player.showTitle(Title.title( 28 Component.text("Welcome!", NamedTextColor.GOLD), 29 Component.text("To " + player.getWorld().getName(), NamedTextColor.YELLOW), 30 Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)) 31)); 32player.sendActionBar(Component.text("Health: " + player.getHealth(), NamedTextColor.RED));

Configuration (YAML)

src/main/resources/config.yml

yaml
1# Default config 2settings: 3 max-players: 20 4 welcome-message: "<green>Welcome to the server!" 5 cooldown-seconds: 30 6 7database: 8 host: localhost 9 port: 3306 10 name: myplugin_db

Accessing config values

java
1// In onEnable(): 2saveDefaultConfig(); // writes config.yml if absent 3 4// Reading values 5int maxPlayers = getConfig().getInt("settings.max-players", 20); 6String message = getConfig().getString("settings.welcome-message", "Welcome!"); 7boolean enabled = getConfig().getBoolean("features.pvp", true); 8 9// Reloading 10reloadConfig(); 11 12// Writing values 13getConfig().set("settings.max-players", 30); 14saveConfig();

Custom config file

java
1File customFile = new File(getDataFolder(), "data.yml"); 2if (!customFile.exists()) { 3 saveResource("data.yml", false); // copies from resources/ 4} 5FileConfiguration customConfig = YamlConfiguration.loadConfiguration(customFile); 6customConfig.set("some.key", "value"); 7customConfig.save(customFile);

Vault Integration (Economy / Permissions)

java
1import net.milkbowl.vault.economy.Economy; 2import org.bukkit.plugin.RegisteredServiceProvider; 3 4public class MyPlugin extends JavaPlugin { 5 private Economy economy; 6 7 @Override 8 public void onEnable() { 9 if (!setupEconomy()) { 10 getLogger().severe("Vault not found! Economy features disabled."); 11 } 12 } 13 14 private boolean setupEconomy() { 15 if (getServer().getPluginManager().getPlugin("Vault") == null) return false; 16 RegisteredServiceProvider<Economy> rsp = 17 getServer().getServicesManager().getRegistration(Economy.class); 18 if (rsp == null) return false; 19 economy = rsp.getProvider(); 20 return economy != null; 21 } 22 23 // Usage 24 public void chargePlayer(Player player, double amount) { 25 if (economy != null && economy.has(player, amount)) { 26 economy.withdrawPlayer(player, amount); 27 } 28 } 29}

Paper-Specific APIs

Async chunk loading

java
1// Paper: load chunk without blocking main thread 2world.getChunkAtAsync(x, z).thenAccept(chunk -> { 3 // runs on main thread after chunk loads 4 chunk.getBlock(0, 64, 0).setType(Material.GOLD_BLOCK); 5});

Custom item meta

java
1// Set custom model data (for resource packs) 2ItemStack item = new ItemStack(Material.STICK); 3ItemMeta meta = item.getItemMeta(); 4meta.setCustomModelData(1001); 5meta.displayName(Component.text("Magic Wand", NamedTextColor.LIGHT_PURPLE)); 6item.setItemMeta(meta);

Player profile (async)

java
1// Paper: async profile lookup (no blocking main thread) 2Bukkit.createProfile(UUID.fromString("...")).update().thenAccept(profile -> { 3 String name = profile.getName(); 4});

GriefPrevention / WorldGuard bypass

java
1// Check if location is protected (WorldGuard example) 2// Always soft-depend on protection plugins 3if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { 4 // use WorldGuard API 5}

Common Tasks Checklist

Creating a new event listener

  • Create class implementing Listener
  • Annotate methods with @EventHandler
  • Call getServer().getPluginManager().registerEvents(listener, plugin) in onEnable()
  • Add ignoreCancelled = true unless you need cancelled events

Adding a new command

  • Define command in plugin.yml under commands:
  • Create executor class implementing CommandExecutor
  • (Optional) implement TabCompleter for autocomplete
  • Register with getCommand("name").setExecutor(new MyExecutor())

Saving plugin data

  • For simple values: use config.yml via getConfig() / saveConfig()
  • For per-entity data: use PDC with a NamespacedKey
  • For large datasets: use async scheduler + file I/O or a database

Scheduling a repeating task

  • Determine if task needs main thread (use runTaskTimer) or is I/O (use runTaskTimerAsynchronously)
  • Store the BukkitTask reference so you can cancel in onDisable()
  • Cancel all tasks in onDisable() or use getServer().getScheduler().cancelTasks(plugin)

Build & Run

bash
1# Build plugin JAR 2./gradlew shadowJar 3 4# Output: build/libs/my-plugin-1.0.0-SNAPSHOT.jar 5# Copy to server/plugins/ and restart the server 6 7# Run Paper dev server (with run-task plugin) 8./gradlew runServer

Validator Script

Use the bundled validator before publishing a Paper plugin:

bash
1# Run from the installed skill directory: 2./scripts/validate-plugin-layout.sh --root /path/to/plugin-project 3 4# Strict mode treats warnings as failures: 5./scripts/validate-plugin-layout.sh --root /path/to/plugin-project --strict

What it checks:

  • plugin.yml required keys (name, version, main, api-version) and repo-supported 1.21 / positive 1.21.<patch> api-version values on the 1.21.x line, with warnings for patches newer than the repo's current example version
  • Main class path exists and extends JavaPlugin
  • /reload anti-pattern detection in source snippets

References

相关技能

寻找 minecraft-plugin-dev 的替代方案 (Alternative) 或可搭配使用的同类 community Skill?探索以下相关开源技能。

查看全部

openclaw-release-maintainer

Logo of openclaw
openclaw

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

333.8k
0
AI

widget-generator

Logo of f
f

Generate customizable widget plugins for the prompts.chat feed system

149.6k
0
AI

flags

Logo of vercel
vercel

The React Framework

138.4k
0
浏览器

pr-review

Logo of pytorch
pytorch

Tensors and Dynamic neural networks in Python with strong GPU acceleration

98.6k
0
开发者工具