minecraft-paper-plugin

Minecraft Paper Plugin Development Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "minecraft-paper-plugin" with this command: npx skills add sun-mc-dev/claude-minecraft-skill/sun-mc-dev-claude-minecraft-skill-minecraft-paper-plugin

Minecraft Paper Plugin Development Skill

Trigger Phrase

This skill activates when the user says "Gigi Aki" followed by their plugin requirements.

Overview

Create production-ready Minecraft Paper plugins following modern best practices:

  • Modern Paper API: No deprecated methods, use latest Paper-specific features

  • PacketEvents: Packet manipulation for custom mechanics and anti-cheat

  • Async & Multi-threaded: Non-blocking operations, proper scheduler usage

  • High Performance: Optimized code, caching, minimal allocations

  • Flexible Database: Support SQLite (default), MySQL, PostgreSQL, MongoDB, Redis

  • Type-safe Configuration: YAML (default) or JSON with validation

  • Modern Dependencies: Lombok, HikariCP, Reflections, Gson, Adventure API

  • Complete Developer API: Full API system for other plugins to integrate

  • Professional Documentation: Auto-generated README.md, API docs, contribution guides

  • GitHub Ready: CI/CD workflows, issue templates, changelog, code of conduct

  • Unit Testing: JUnit 5 test structure with examples

  • Code Quality: EditorConfig, consistent formatting, best practices

Development Workflow

  1. Analyze Requirements

When user says "Gigi Aki" + requirements:

  • Identify plugin type (combat, economy, game mechanic, anti-cheat, etc.)

  • Determine complexity and required features

  • Choose appropriate architecture patterns

  • Select database type (default SQLite unless specified)

  • Choose build system (Maven default, Gradle if multi-module)

  • Determine Java version (Java 21 for modern, Java 8 for legacy 1.8)

1.5. Interactive Requirements Gathering

IMPORTANT: Before implementing, engage in a detailed requirements gathering session like an experienced developer. Ask clarifying questions to understand the full scope and details. Think like a senior Java developer with 25 years of experience who needs complete specifications before writing production code.

Core Functionality Questions

For ANY plugin type, ask:

  • Target server version: "What Minecraft version are you targeting?" (1.21.x, 1.20.x, 1.8.8, etc.)

  • Scale and performance: "How many players will this handle concurrently?" (affects architecture decisions)

  • Persistence requirements: "What data needs to be saved?" (player stats, configurations, logs, etc.)

  • Multi-server support: "Will this run on a single server or network (BungeeCord/Velocity)?"

Plugin-Specific Deep Dive Questions

For Combat Plugins:

  • "What combat mechanics do you want to modify?" (knockback, damage, cooldowns, combos, hit detection)

  • "Should it work with vanilla combat or completely override it?"

  • "Any specific formulas for damage/knockback calculations?"

  • "Do you need hitbox manipulation or just damage/velocity modification?"

  • "Should there be weapon-specific mechanics?" (swords vs axes vs bows)

  • "Any particle effects or sound effects for hits?"

  • "Do you need combat logging prevention? How long is the combat tag?"

  • "Should there be different modes/arenas with different rules?"

For Economy Plugins:

  • "What currency system?" (single currency, multiple currencies, item-based)

  • "Starting balance for new players?"

  • "Transaction types needed?" (player-to-player, player-to-shop, admin commands)

  • "Should there be a transaction fee/tax system?"

  • "Do you need bank accounts, or just wallet balances?"

  • "Interest on balances? Offline earning?"

  • "Integration with existing plugins?" (Vault API, other economy plugins)

  • "Admin tools needed?" (set balance, add/remove money, transaction history, economy reset)

  • "GUI shops or command-based?"

For Anti-Cheat Plugins:

  • "Which specific cheats to detect?" (killaura, reach, speed, fly, scaffold, etc.)

  • "Detection approach?" (statistical analysis, pattern matching, threshold-based)

  • "What actions on violation?" (kick, ban, notify admins, rollback, reduce damage)

  • "Violation decay system?" (violations expire after time)

  • "Bypass permissions for staff?"

  • "Should it log to database for analysis?"

  • "Integration with existing ban plugins?"

  • "False positive handling - how strict vs lenient?"

  • "Client brand/mod detection?"

For Parkour Plugins:

  • "Checkpoint system?" (automatic, manual, pressure plates)

  • "How to define parkour courses?" (worldedit selection, config, in-game commands)

  • "Leaderboard requirements?" (per-course, global, time-based, completion-based)

  • "Rewards system?" (money, items, permissions on completion)

  • "Practice mode vs competitive mode?"

  • "Spectator system for other players?"

  • "Reset mechanism?" (command, automatic on fall, checkpoint rollback)

  • "Time tracking precision?" (milliseconds, seconds)

For Game Mechanic Plugins:

  • "What's the core game loop?"

  • "Win/loss conditions?"

  • "Team system or free-for-all?"

  • "Lobby system needed?"

  • "Game phases?" (waiting, starting, active, ending)

  • "Arena/map management?"

  • "Spectator mode?"

  • "Statistics tracking?"

For Shop/GUI Plugins:

  • "Item categories and organization?"

  • "Dynamic pricing or fixed?"

  • "Stock limits? Cooldowns?"

  • "Permission-based access to items?"

  • "Sell-back system?"

  • "GUI layout preferences?" (chest size, navigation style)

  • "Search functionality?"

  • "Transaction confirmation dialogs?"

Architecture and Technical Questions

Always ask:

  • Commands structure: "What commands are needed? Include aliases, permissions, and usage examples."

  • Permissions hierarchy: "How should permissions be organized?" (admin, moderator, user tiers)

  • Configuration flexibility: "What should be configurable vs hard-coded?"

  • API exposure: "Will other plugins need to hook into this? Need a developer API?"

  • Event priorities: "Any specific event handling priorities or conflicts with other plugins?"

  • Placeholder support: "Need PlaceholderAPI integration for displaying data?"

  • Locale/i18n: "Multiple languages needed? What's the default?"

Performance and Scalability Questions

For production plugins, ask:

  • Expected TPS impact: "What's your performance budget? Target TPS with plugin active?"

  • Data volume: "How much data per player? How many players?"

  • Caching strategy: "What data should be cached in memory vs fetched from DB?"

  • Async requirements: "Which operations MUST be async?" (database writes, external API calls)

  • Batch processing: "Any bulk operations that need optimization?"

Example Complete Requirements Session

User: "Gigi Aki - create a custom knockback plugin"

Assistant: "I'll create a professional knockback plugin for you. Let me gather some details to ensure it meets your exact needs:

Core Functionality:

  • What Minecraft version are you targeting?

  • Should this modify vanilla knockback or completely override it?

  • What parameters do you want to configure? (horizontal multiplier, vertical multiplier, per-world settings, per-weapon settings)

  • Do you need different knockback profiles (PvP mode, practice mode, etc.)?

Packet Implementation: 5. Should I use PacketEvents for precise velocity control, or modify through Bukkit events? 6. Do you want client-side prediction compensation?

Features: 7. Should knockback vary by weapon type? (swords, axes, custom items) 8. Do you need sprint/velocity multipliers? 9. Any special effects? (particles, sounds on hit) 10. Need a test command for admins to try different values live?

Data & Permissions: 11. Should player preferences be saved? (players can choose their knockback style) 12. Permission nodes for bypassing custom knockback? 13. Any integration with combat plugins or anti-cheat?

Performance: 14. How many players will be in combat simultaneously? 15. Should calculations be cached or computed per-hit?

Once you provide these details, I'll create a production-ready plugin with complete implementation, no placeholders."

Requirements Documentation

After gathering requirements, create a brief summary before implementation:

=== PLUGIN REQUIREMENTS SUMMARY === Type: [Combat/Economy/etc.] Target Version: [1.21.3/1.8.8/etc.] Java Version: [21/8] Build Tool: [Maven/Gradle] Database: [SQLite/MySQL/etc.]

Core Features:

  • Feature 1: [detailed description]
  • Feature 2: [detailed description]

Commands:

  • /command1 <args> - Description [permission.node]

Configuration:

  • setting1: default value
  • setting2: default value

Performance Requirements:

  • Max players: X
  • Data per player: Y
  • Async operations: [list]

Special Considerations:

  • [any unique requirements] ===================================

This ensures both you and the user are aligned before writing a single line of code.

  1. Load Relevant References

Based on plugin requirements, read appropriate reference files:

  • Always read:

  • references/professional-template.md for project structure, README, API, GitHub setup

  • references/paper-api-patterns.md for modern Paper API usage

  • For packet manipulation: references/packetevents-patterns.md

  • For database features: references/database-patterns.md

  • For configuration: references/config-patterns.md

  • For Maven projects: references/maven-template.md

  • For Gradle projects: references/gradle-template.md

  1. Create Complete Implementation

Generate full plugin structure with:

Project Files

Maven Project (Default):

plugin-name/ ├── .github/ │ ├── workflows/ │ │ ├── build.yml # CI/CD build pipeline │ │ └── release.yml # Release automation │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md # Bug report template │ │ └── feature_request.md # Feature request template │ └── dependabot.yml # Dependency updates ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── [package]/ │ │ │ ├── PluginName.java # Main plugin class │ │ │ ├── api/ # Public API package │ │ │ │ ├── PluginNameAPI.java │ │ │ │ ├── events/ # Custom events │ │ │ │ ├── managers/ # Manager interfaces │ │ │ │ └── data/ # Data classes │ │ │ ├── commands/ # Command classes │ │ │ ├── listeners/ # Event listeners │ │ │ ├── managers/ # Manager implementations │ │ │ ├── database/ # Database layer │ │ │ ├── config/ # Configuration │ │ │ └── util/ # Utilities │ │ └── resources/ │ │ ├── plugin.yml │ │ ├── config.yml │ │ └── messages.yml # i18n messages │ └── test/ │ └── java/ │ └── [package]/ │ └── PluginNameTest.java # Unit tests ├── .editorconfig # Code style consistency ├── .gitignore # Git ignore rules ├── CHANGELOG.md # Version history ├── CODE_OF_CONDUCT.md # Community guidelines ├── CONTRIBUTING.md # Contribution guide ├── LICENSE # MIT License ├── README.md # Project documentation └── pom.xml # Maven configuration

Gradle Project (for Kotlin or multi-module):

plugin-name/ ├── build.gradle.kts ├── settings.gradle.kts ├── gradle/ │ └── libs.versions.toml └── src/ (same structure as Maven)

Essential Files to Generate

CRITICAL: Every plugin MUST include these files for a professional project:

  • README.md - Complete project documentation (see professional-template.md)

  • API Package - Full developer API with:

  • api/PluginNameAPI.java (interface)

  • PluginNameAPIImpl.java (implementation, package-private)

  • api/events/ (custom events)

  • api/managers/ (manager interfaces)

  • GitHub Workflows:

  • .github/workflows/build.yml (CI/CD)

  • .github/workflows/release.yml (auto-releases)

  • Issue Templates:

  • .github/ISSUE_TEMPLATE/bug_report.md

  • .github/ISSUE_TEMPLATE/feature_request.md

  • Contributing Files:

  • CONTRIBUTING.md

  • CODE_OF_CONDUCT.md

  • CHANGELOG.md

  • Code Quality:

  • .editorconfig

  • .gitignore

  • Unit Tests:

  • src/test/java/.../PluginNameTest.java

  • LICENSE - MIT License

Reference: See references/professional-template.md for complete templates of all these files.

Core Components

Main Plugin Class (with API integration):

@Getter public class PluginName extends JavaPlugin { @Getter private static PluginName instance;

@Getter
private static PluginNameAPI api;

private ConfigManager&#x3C;PluginConfig> configManager;
private DatabaseManager database;

@Override
public void onLoad() {
    instance = this;
    // Initialize PacketEvents
    PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
    PacketEvents.getAPI().load();
}

@Override
public void onEnable() {
    long startTime = System.currentTimeMillis();
    
    try {
        // Initialize PacketEvents
        PacketEvents.getAPI().init();
        
        // Load configuration
        configManager = new YamlConfigManager(this);
        PluginConfig config = configManager.get();
        
        // Validate configuration
        if (!ConfigValidator.validate(config, this)) {
            getLogger().severe("Invalid configuration!");
            getServer().getPluginManager().disablePlugin(this);
            return;
        }
        
        // Initialize database
        database = DatabaseFactory.create(this, getConfig());
        database.initialize().join();
        
        // Register listeners
        registerListeners();
        
        // Register commands
    registerCommands();
    
    // Start async tasks
    startTasks();
    
    // Initialize API (last step, after all managers are ready)
    api = new PluginNameAPIImpl();
    
    long loadTime = System.currentTimeMillis() - startTime;
    getLogger().info("Plugin enabled successfully in " + loadTime + "ms!");
    } catch (Exception e) {
        getLogger().log(Level.SEVERE, "Failed to enable plugin!", e);
        getServer().getPluginManager().disablePlugin(this);
    }
}

@Override
public void onDisable() {
    getLogger().info("Disabling plugin...");
    
    // Shutdown PacketEvents
    PacketEvents.getAPI().terminate();
    
    // Shutdown database (save all data synchronously)
    if (database != null) {
        database.shutdown().join();
    }
    
    getLogger().info("Plugin disabled successfully!");
}

private void registerListeners() {
    // Bukkit listeners
    getServer().getPluginManager().registerEvents(new YourListener(this), this);
    
    // PacketEvents listeners
    PacketEvents.getAPI().getEventManager().registerListener(new YourPacketListener(this));
}

private void registerCommands() {
    // Register commands
}

private void startTasks() {
    // Start async repeating tasks
}

}

  1. Implementation Guidelines

Always Use Modern APIs

// CORRECT - Modern Paper scheduler plugin.getServer().getAsyncScheduler().runNow(plugin, task -> { // Async work });

// WRONG - Deprecated BukkitScheduler Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {});

Packet-Based Features

For custom mechanics, use PacketEvents:

public class CustomKnockbackListener extends PacketListenerAbstract { @Override public void onPacketSend(PacketSendEvent event) { if (event.getPacketType() == PacketType.Play.Server.ENTITY_VELOCITY) { WrapperPlayServerEntityVelocity packet = new WrapperPlayServerEntityVelocity(event);

        // Modify velocity
        Vector3d velocity = packet.getVelocity();
        packet.setVelocity(new Vector3d(
            velocity.x * 1.5,
            velocity.y * 1.2,
            velocity.z * 1.5
        ));
    }
}

}

Developer API Implementation

CRITICAL: Every plugin must include a complete, well-documented API for other plugins to integrate with.

API Interface (api/PluginNameAPI.java ):

package com.example.plugin.api;

/**

  • Main API interface for PluginName

  • <p>

  • Access via: PluginName.getApi()

  • <p>

  • This API is guaranteed to be stable across minor versions.

  • Breaking changes will only occur in major version updates.

  • @since 1.0.0 */ public interface PluginNameAPI {

    /**

    • Get the API version
    • @return API version string in format "major.minor.patch" */ String getApiVersion();

    /**

    • Check if the plugin is fully loaded and ready to use
    • @return true if ready, false during startup/shutdown */ boolean isReady();

    // Add your API methods here with full JavaDoc // Example: /**

    • Get player data for the specified player
    • @param uuid Player UUID
    • @return CompletableFuture containing player data, or null if not found */ // CompletableFuture<PlayerData> getPlayerData(UUID uuid); }

API Implementation (PluginNameAPIImpl.java , package-private):

package com.example.plugin;

import com.example.plugin.api.PluginNameAPI;

/**

  • Internal implementation of the API

  • <p>

  • Do not use this class directly - use PluginNameAPI interface instead */ class PluginNameAPIImpl implements PluginNameAPI {

    @Override public String getApiVersion() { return "1.0.0"; }

    @Override public boolean isReady() { return PluginName.getInstance() != null && PluginName.getInstance().isEnabled(); }

    // Implement your API methods here }

Custom Events (api/events/CustomEvent.java ):

package com.example.plugin.api.events;

import lombok.Getter; import lombok.Setter; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull;

/**

  • Called when [describe when this event is called]

  • <p>

  • This event is cancellable. Cancelling it will prevent [action].

  • @since 1.0.0 */ @Getter public class CustomEvent extends Event implements Cancellable {

    private static final HandlerList HANDLERS = new HandlerList();

    private final Player player; // Add your event data fields

    @Setter private boolean cancelled;

    public CustomEvent(Player player) { super(true); // async if needed this.player = player; }

    @NotNull @Override public HandlerList getHandlers() { return HANDLERS; }

    @NotNull public static HandlerList getHandlerList() { return HANDLERS; } }

Calling Custom Events:

// Fire your custom events CustomEvent event = new CustomEvent(player); Bukkit.getPluginManager().callEvent(event);

if (event.isCancelled()) { return; // Another plugin cancelled it } // Continue with action

Database Operations

Always async with CompletableFuture:

database.save("player:" + uuid, playerData) .thenAccept(success -> { if (success) { player.sendMessage("Data saved!"); } });

Configuration Management

Type-safe with Lombok:

@Data @Builder public class PluginConfig { private boolean enabled; private String prefix; private DatabaseConfig database; private CombatConfig combat; }

  1. Quality Standards

Every plugin must include:

Complete API System:

  • Public API interface (api/PluginNameAPI.java )

  • Package-private implementation

  • Custom events in api/events/

  • Full JavaDoc on all public methods

  • Example usage in README.md

Professional Documentation:

  • README.md with features, installation, commands, API usage

  • CONTRIBUTING.md for contributors

  • CHANGELOG.md following Keep a Changelog format

  • CODE_OF_CONDUCT.md

  • Issue templates for bugs and features

GitHub Actions CI/CD:

  • .github/workflows/build.yml for automated builds

  • .github/workflows/release.yml for automated releases

  • Dependabot for dependency updates

Unit Tests:

  • JUnit 5 test structure

  • Test core functionality

  • Mock Bukkit/Paper API appropriately

Code Quality:

  • Use zero deprecated APIs

  • Handle all operations asynchronously where appropriate

  • Include proper error handling and logging

  • Use concurrent collections for thread safety

  • Implement proper cleanup in onDisable()

  • Include configuration validation

  • Use Lombok to reduce boilerplate

  • Follow .editorconfig formatting

Follow modern Java patterns (records, switch expressions where appropriate)

Include proper plugin.yml with api-version

Implement complete, working features (no placeholders)

  1. Plugin.yml Template

name: PluginName version: ${project.version} main: com.example.pluginname.PluginName api-version: '1.21' author: YourName description: Plugin description website: https://example.com

Load order

load: POSTWORLD

or load: STARTUP for early loading

Dependencies (optional)

depend: [] softdepend: [] loadbefore: []

Commands (if any)

commands: commandname: description: Command description usage: /<command> [args] permission: pluginname.command aliases: [alias1, alias2]

Permissions (optional)

permissions: pluginname.admin: description: Admin permission default: op pluginname.use: description: Basic usage permission default: true

Example Plugin Types

Combat Plugin

  • Custom knockback with PacketEvents

  • Hit detection and validation

  • Combat cooldowns

  • Damage modifiers

  • Anti-cheat integration

Economy Plugin

  • Database-backed player balances

  • Transaction system with async operations

  • Shop GUI with Adventure API

  • Multi-currency support

  • Transaction history

Game Mechanic Plugin

  • Custom entity behavior

  • Parkour systems with checkpoints

  • Mini-game framework

  • Event-driven mechanics

  • Async world manipulation

Anti-Cheat Plugin

  • Packet-based movement checks

  • Combat analysis (reach, killaura, etc.)

  • Speed and fly detection

  • Violation tracking and punishment

  • Analytics and logging

Performance Optimization

Cache frequently accessed data

private final Map<UUID, PlayerData> cache = new ConcurrentHashMap<>();

Use async for I/O operations

plugin.getServer().getAsyncScheduler().runNow(plugin, task -> { // Database or file operations });

Minimize allocations in hot paths

// Reuse objects, use primitive collections where appropriate

Use packet manipulation instead of events when possible

// PacketEvents is more performant than Bukkit events for certain tasks

Batch database operations

// Save multiple records in one transaction

Common Patterns

Manager Pattern

public class PlayerManager { private final Map<UUID, PlayerData> players = new ConcurrentHashMap<>();

public CompletableFuture&#x3C;PlayerData> loadPlayer(UUID uuid) {
    return database.load("player:" + uuid)
        .thenApply(opt -> {
            PlayerData data = opt.orElse(new PlayerData(uuid));
            players.put(uuid, data);
            return data;
        });
}

public CompletableFuture&#x3C;Void> savePlayer(UUID uuid) {
    PlayerData data = players.get(uuid);
    if (data != null) {
        return database.save("player:" + uuid, data).thenAccept(success -> {});
    }
    return CompletableFuture.completedFuture(null);
}

}

Command Pattern with Reflections

public abstract class BaseCommand implements CommandExecutor { protected final JavaPlugin plugin;

public BaseCommand(JavaPlugin plugin) {
    this.plugin = plugin;
}

}

// Auto-register all commands Reflections reflections = new Reflections("com.example.pluginname.commands"); Set<Class<? extends BaseCommand>> commands = reflections.getSubTypesOf(BaseCommand.class);

for (Class<? extends BaseCommand> cmdClass : commands) { BaseCommand cmd = cmdClass.getConstructor(JavaPlugin.class).newInstance(plugin); // Register command }

Final Notes

  • Complete implementations only - No TODO comments or placeholder code

  • Test critical paths - Ensure database operations, packet handling work correctly

  • Modern Java features - Use records, pattern matching, text blocks where appropriate

  • Proper logging - Use plugin logger for important events

  • Clean code - Follow Java naming conventions, proper indentation

  • Documentation - Add Javadocs for public APIs and complex methods

Advanced Scenarios and Edge Cases

As an experienced developer, always consider and implement solutions for:

Concurrency and Thread Safety

Race Conditions:

  • Use ConcurrentHashMap for shared mutable state

  • Consider AtomicInteger , AtomicReference for counters/flags

  • Use synchronized blocks or ReentrantLock only when necessary

  • Prefer immutable objects (records, final fields)

Example:

// GOOD - Thread-safe counter private final AtomicInteger activeGames = new AtomicInteger(0);

// GOOD - Immutable data transfer public record PlayerStats(UUID uuid, int kills, int deaths, double kdr) {}

// CAREFUL - Synchronized block only for compound operations private final Map<UUID, Integer> scores = new ConcurrentHashMap<>(); public void addScore(UUID player, int amount) { scores.merge(player, amount, Integer::sum); // Thread-safe }

Memory Leaks Prevention

Common causes:

  • Listeners not unregistered - Store listener instances, unregister in onDisable()

  • Scheduled tasks not cancelled - Cancel all tasks in onDisable()

  • Player data not cleaned up - Remove on PlayerQuitEvent

  • Static collections - Clear in onDisable()

  • Event handler references - Use WeakHashMap if caching players/entities

Example:

private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();

public void startTask(Runnable task) { BukkitTask scheduled = plugin.getServer().getScheduler()...; tasks.add(scheduled); }

@Override public void onDisable() { // Cancel all tasks tasks.forEach(BukkitTask::cancel); tasks.clear();

// Clear player cache
playerCache.clear();

// Unregister listeners if dynamically registered

}

Database Transaction Safety

Handle failures gracefully:

public CompletableFuture<Boolean> transferMoney(UUID from, UUID to, double amount) { return CompletableFuture.supplyAsync(() -> { Connection conn = null; try { conn = dataSource.getConnection(); conn.setAutoCommit(false); // Start transaction

        // Deduct from sender
        boolean deducted = deductBalance(conn, from, amount);
        if (!deducted) {
            conn.rollback();
            return false;
        }
        
        // Add to receiver
        boolean added = addBalance(conn, to, amount);
        if (!added) {
            conn.rollback();
            return false;
        }
        
        conn.commit(); // Commit transaction
        return true;
        
    } catch (SQLException e) {
        if (conn != null) {
            try {
                conn.rollback();
            } catch (SQLException ex) {
                plugin.getLogger().severe("Failed to rollback: " + ex.getMessage());
            }
        }
        plugin.getLogger().severe("Transaction failed: " + e.getMessage());
        return false;
    } finally {
        if (conn != null) {
            try {
                conn.setAutoCommit(true);
                conn.close();
            } catch (SQLException e) {
                // Log but don't throw
            }
        }
    }
});

}

Packet Event Edge Cases

Handle cancelled events:

@Override public void onPacketReceive(PacketReceiveEvent event) { // Check if already cancelled by another listener if (event.isCancelled()) { return; }

// Your logic here

// Cancel with reason
if (shouldCancel) {
    event.setCancelled(true);
    // Optionally notify player
    Player player = (Player) event.getPlayer();
    if (player != null) {
        player.sendMessage("Action blocked: " + reason);
    }
}

}

Handle rapid packet spam:

private final Map<UUID, RateLimiter> packetLimiters = new ConcurrentHashMap<>();

@Override public void onPacketReceive(PacketReceiveEvent event) { Player player = (Player) event.getPlayer(); UUID uuid = player.getUniqueId();

RateLimiter limiter = packetLimiters.computeIfAbsent(uuid, 
    k -> new RateLimiter(50, 1000)); // 50 packets per second

if (!limiter.tryAcquire()) {
    event.setCancelled(true);
    plugin.getLogger().warning(player.getName() + " is sending packets too quickly!");
    return;
}

// Process packet normally

}

Configuration Migration

Handle config version changes:

public class ConfigMigration { private static final int CURRENT_VERSION = 3;

public static void migrate(FileConfiguration config, JavaPlugin plugin) {
    int version = config.getInt("config-version", 1);
    
    if (version &#x3C; CURRENT_VERSION) {
        plugin.getLogger().info("Migrating config from v" + version + " to v" + CURRENT_VERSION);
        
        if (version &#x3C; 2) {
            migrateV1ToV2(config);
        }
        if (version &#x3C; 3) {
            migrateV2ToV3(config);
        }
        
        config.set("config-version", CURRENT_VERSION);
        
        try {
            config.save(new File(plugin.getDataFolder(), "config.yml"));
        } catch (IOException e) {
            plugin.getLogger().severe("Failed to save migrated config: " + e.getMessage());
        }
    }
}

private static void migrateV1ToV2(FileConfiguration config) {
    // Rename old settings
    if (config.contains("old-setting")) {
        config.set("new-setting", config.get("old-setting"));
        config.set("old-setting", null);
    }
    
    // Add new default values
    config.addDefault("new-feature.enabled", true);
}

}

Graceful Plugin Reload

Handle /reload command:

@Override public void onDisable() { // Save all pending data SYNCHRONOUSLY saveAllData().join(); // Wait for completion

// Cancel tasks
cancelAllTasks();

// Close connections
closeConnections();

getLogger().info("Plugin disabled safely");

}

@Override public void onEnable() { // Check if this is a reload if (isReloading()) { getLogger().warning("Plugin reloaded. Some features may not work correctly. Restart recommended."); }

// Re-initialize everything

}

private boolean isReloading() { // Check if players are online (would be 0 on first server start) return !Bukkit.getOnlinePlayers().isEmpty(); }

API Version Compatibility

Handle different Paper versions:

public class VersionAdapter { private static final String VERSION;

static {
    String packageName = Bukkit.getServer().getClass().getPackage().getName();
    VERSION = packageName.substring(packageName.lastIndexOf('.') + 1);
}

public static boolean isModernVersion() {
    // 1.20.5+ has different packet structure
    return VERSION.compareTo("v1_20_R4") >= 0;
}

public static void sendActionBar(Player player, String message) {
    if (isModernVersion()) {
        player.sendActionBar(Component.text(message));
    } else {
        // Legacy method
        player.spigot().sendMessage(ChatMessageType.ACTION_BAR, 
            TextComponent.fromLegacyText(message));
    }
}

}

Error Recovery Patterns

Implement circuit breaker for external services:

public class CircuitBreaker { private final int threshold; private final long timeout; private AtomicInteger failures = new AtomicInteger(0); private volatile long lastFailureTime = 0; private volatile boolean open = false;

public boolean isOpen() {
    if (open &#x26;&#x26; System.currentTimeMillis() - lastFailureTime > timeout) {
        open = false; // Try again after timeout
        failures.set(0);
    }
    return open;
}

public void recordSuccess() {
    failures.set(0);
    open = false;
}

public void recordFailure() {
    lastFailureTime = System.currentTimeMillis();
    if (failures.incrementAndGet() >= threshold) {
        open = true;
    }
}

}

// Usage in database operations public CompletableFuture<Boolean> save(String key, Object value) { if (circuitBreaker.isOpen()) { plugin.getLogger().warning("Circuit breaker open, skipping database operation"); return CompletableFuture.completedFuture(false); }

return CompletableFuture.supplyAsync(() -> {
    try {
        // Database operation
        circuitBreaker.recordSuccess();
        return true;
    } catch (Exception e) {
        circuitBreaker.recordFailure();
        plugin.getLogger().severe("Database operation failed: " + e.getMessage());
        return false;
    }
});

}

Performance Monitoring

Add built-in performance metrics:

public class PerformanceMonitor { private final Map<String, LongAdder> operationCounts = new ConcurrentHashMap<>(); private final Map<String, LongAdder> operationTimes = new ConcurrentHashMap<>();

public void recordOperation(String operation, long durationNanos) {
    operationCounts.computeIfAbsent(operation, k -> new LongAdder()).increment();
    operationTimes.computeIfAbsent(operation, k -> new LongAdder())
        .add(durationNanos);
}

public void printStats() {
    plugin.getLogger().info("=== Performance Stats ===");
    operationCounts.forEach((op, count) -> {
        long totalTime = operationTimes.get(op).sum();
        long avgTime = totalTime / count.sum();
        plugin.getLogger().info(String.format("%s: %d calls, avg %d μs",
            op, count.sum(), avgTime / 1000));
    });
}

}

// Usage long start = System.nanoTime(); try { // Your operation } finally { performanceMonitor.recordOperation("database.save", System.nanoTime() - start); }

These advanced patterns should be implemented where appropriate based on the plugin's complexity and requirements.

Documentation and Project Delivery

CRITICAL: Complete Project Checklist

When delivering a plugin, ALWAYS generate ALL of these files:

  1. Source Code Files
  • ✅ All Java source files in proper package structure

  • ✅ Complete API package with interfaces and events

  • ✅ Unit tests in src/test/

  • ✅ plugin.yml with all metadata

  • ✅ config.yml with all configuration options

  • ✅ messages.yml (if using messages)

  1. Build Configuration
  • ✅ pom.xml or build.gradle.kts with all dependencies

  • ✅ Maven wrapper files (mvnw , mvnw.cmd ) or Gradle wrapper

  1. Documentation Files
  • ✅ README.md - Complete project documentation with:

  • Project description and features

  • Installation instructions

  • Configuration guide

  • Commands and permissions tables

  • API usage examples

  • Links to wiki/Discord/issues

  • Badges (build status, license, version)

  • ✅ CHANGELOG.md - Version history following Keep a Changelog

  • ✅ CONTRIBUTING.md - How to contribute

  • ✅ CODE_OF_CONDUCT.md - Community guidelines

  • ✅ LICENSE - MIT License (or as specified)

  1. GitHub Configuration
  • ✅ .github/workflows/build.yml

  • CI/CD build pipeline

  • ✅ .github/workflows/release.yml

  • Automated releases

  • ✅ .github/ISSUE_TEMPLATE/bug_report.md

  • Bug report template

  • ✅ .github/ISSUE_TEMPLATE/feature_request.md

  • Feature request template

  • ✅ .github/dependabot.yml

  • Dependency updates

  1. Code Quality Files
  • ✅ .editorconfig

  • Code formatting rules

  • ✅ .gitignore

  • Git ignore rules

README.md Generation Guidelines

ALWAYS customize the README.md with:

Replace all placeholders:

  • PluginName → actual plugin name

  • username → GitHub username

  • [Feature descriptions] → actual features

  • Example commands → actual commands from plugin

  • Example permissions → actual permissions

  • Discord/support links → real links or remove

Add accurate feature list based on what the plugin actually does

Include complete command/permission tables with all commands implemented

Write API usage examples using the actual API methods created

Add relevant badges:

Build Status License: MIT Paper

Include configuration example from the actual config.yml

CHANGELOG.md Initialization

Start with:

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

Added

  • Initial plugin features

1.0.0 - YYYY-MM-DD

Added

  • [List all initial features]
  • Complete developer API
  • Database support (SQLite/MySQL/PostgreSQL)
  • Configuration system
  • [Specific feature 1]
  • [Specific feature 2]

Unit Test Example

Always include at least one test:

package com.example.plugin;

import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.*;

class PluginNameTest {

@BeforeEach
void setUp() {
    // Setup mocks if needed
}

@Test
void testApiVersion() {
    // Example test - adapt to your plugin
    assertEquals("1.0.0", "1.0.0");
}

@Test
void testConfigValidation() {
    // Test configuration validation
    assertTrue(true);
}

}

File Organization

Organize generated files in the output directory:

/mnt/user-data/outputs/PluginName/ ├── src/ │ ├── main/ │ │ ├── java/... │ │ └── resources/... │ └── test/... ├── .github/... ├── README.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── pom.xml ├── .editorconfig ├── .gitignore └── mvnw / mvnw.cmd

Presentation to User

After generating all files, present them with:

I've created a complete, production-ready [PluginName] plugin with:

✅ Full source code with API system ✅ Complete documentation (README, CHANGELOG, CONTRIBUTING) ✅ GitHub Actions CI/CD ✅ Unit test structure ✅ Issue templates and code of conduct

The plugin includes:

  • [Feature 1]
  • [Feature 2]
  • Complete developer API for integrations
  • [Database type] database support
  • Async operations throughout

To use:

  1. Build: mvn clean package
  2. Find JAR in target/ directory
  3. Place in your server's plugins/ folder

For development:

  • API usage examples in README.md
  • Extend the API by adding methods to PluginNameAPI interface
  • Add custom events in api/events/ package

All files are ready for GitHub - just create a repository and push!

Reference Templates

For complete file templates, ALWAYS refer to references/professional-template.md which contains:

  • Complete README.md template

  • GitHub Actions workflows

  • Issue templates

  • All documentation files

  • API structure examples

This ensures consistent, professional output for every plugin!

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

Github Push

Secure GitHub push automation with auto SSH and remote config. Use when git push, automated push, or conflict handling needed.

Registry SourceRecently Updated
Coding

Scrapling Fetch

支持自动绕过 Cloudflare Turnstile 和微信公众号反爬机制的网页内容抓取工具,输出干净Markdown或纯文本。

Registry SourceRecently Updated
Coding

rapidapi

Template-driven RapidAPI client with auto-registered actions and a universal call entrypoint

Registry SourceRecently Updated