Bukkit API is the foundation for Minecraft server plugin development, enabling extensive customization and feature addition. This comprehensive guide covers plugin creation, event handling, world manipulation, and advanced server modifications.
Bukkit Architecture Overview
Bukkit Plugin Ecosystem: ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Minecraft │ -> │ Bukkit API │ -> │ Your Plugin │ │ Server │ │ • Events │ │ • Listeners │ │ │ │ • Commands │ │ • Commands │ │ │ │ • Scheduler │ │ • Tasks │ └─────────────────┘ └──────────────────┘ └─────────────────┘ │ │ │ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ World │ │ Entities │ │ Players │ │ Manipulation │ │ Management │ │ Interaction │ └─────────────────┘ └──────────────────┘ └─────────────────┘
Project Setup and Dependencies
1. Maven Configuration
<!-- pom.xml --> <properties> <bukkit.version>1.20.1-R0.1-SNAPSHOT</bukkit.version> </properties> <repositories> <repository> <id>spigotmc-repo</id> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> </repository> <repository> <id>sonatype</id> <url>https://oss.sonatype.org/content/groups/public/</url> </repository> </repositories> <dependencies> <!-- Bukkit API --> <dependency> <groupId>org.bukkit</groupId> <artifactId>bukkit</artifactId> <version>${bukkit.version}</version> <scope>provided</scope> </dependency> <!-- Optional: WorldEdit for advanced world manipulation --> <dependency> <groupId>com.sk89q.worldedit</groupId> <artifactId>worldedit-bukkit</artifactId> <version>7.2.9</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> 2. Plugin Configuration File
# src/main/resources/plugin.yml name: MyAwesomePlugin version: 1.0.0 main: com.myplugin.MyAwesomePlugin api-version: 1.20 description: An awesome Minecraft plugin author: YourName website: https://yourwebsite.com commands: hello: description: Say hello to the player usage: /hello permission: myplugin.hello warp: description: Teleport to a warp location usage: /warp <name> permission: myplugin.warp kit: description: Get a kit of items usage: /kit <name> permission: myplugin.kit permissions: myplugin.*: description: All permissions for MyAwesomePlugin children: myplugin.hello: true myplugin.warp: true myplugin.kit: true myplugin.admin: true myplugin.hello: description: Allows using hello command default: true myplugin.warp: description: Allows using warp command default: true myplugin.kit: description: Allows using kit command default: op myplugin.admin: description: Admin permissions default: op
Core Plugin Implementation
1. Main Plugin Class
package com.myplugin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.configuration.file.FileConfiguration; import java.util.logging.Logger; public class MyAwesomePlugin extends JavaPlugin { private static MyAwesomePlugin instance; private Logger logger; private PluginManager pluginManager; private DataManager dataManager; @Override public void onEnable() { instance = this; logger = getLogger(); pluginManager = new PluginManager(this); dataManager = new DataManager(this); // Load configuration saveDefaultConfig(); reloadConfig(); // Initialize managers pluginManager.initialize(); dataManager.initialize(); // Register events registerEvents(); // Register commands registerCommands(); // Start tasks startTasks(); logger.info("MyAwesomePlugin has been enabled successfully!"); } @Override public void onDisable() { // Save data if (dataManager != null) { dataManager.saveAll(); } // Cancel tasks getServer().getScheduler().cancelTasks(this); logger.info("MyAwesomePlugin has been disabled."); } private void registerEvents() { getServer().getPluginManager().registerEvents(new PlayerListener(), this); getServer().getPluginManager().registerEvents(new WorldListener(), this); getServer().getPluginManager().registerEvents(new EntityListener(), this); } private void registerCommands() { getCommand("hello").setExecutor(new HelloCommand()); getCommand("warp").setExecutor(new WarpCommand()); getCommand("kit").setExecutor(new KitCommand()); getCommand("myplugin").setExecutor(new MainCommand()); } private void startTasks() { // Auto-save task every 5 minutes getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { dataManager.saveAll(); }, 6000L, 6000L); // 5 minutes * 60 seconds * 20 ticks // Cleanup task every hour getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { pluginManager.cleanup(); }, 72000L, 72000L); // 1 hour * 60 minutes * 60 seconds * 20 ticks } public static MyAwesomePlugin getInstance() { return instance; } public PluginManager getPluginManager() { return pluginManager; } public DataManager getDataManager() { return dataManager; } } 2. Plugin Manager
package com.myplugin; import org.bukkit.plugin.java.JavaPlugin; import java.util.*; public class PluginManager { private final JavaPlugin plugin; private final Map<String, Warp> warps; private final Map<String, Kit> kits; private final Set<UUID> hiddenPlayers; public PluginManager(JavaPlugin plugin) { this.plugin = plugin; this.warps = new HashMap<>(); this.kits = new HashMap<>(); this.hiddenPlayers = new HashSet<>(); } public void initialize() { loadWarps(); loadKits(); } public void cleanup() { // Clean up expired data, remove unused warps, etc. warps.entrySet().removeIf(entry -> !entry.getValue().isValid()); } // Warp management public void addWarp(String name, Warp warp) { warps.put(name.toLowerCase(), warp); } public Warp getWarp(String name) { return warps.get(name.toLowerCase()); } public boolean removeWarp(String name) { return warps.remove(name.toLowerCase()) != null; } public Set<String> getWarpNames() { return Collections.unmodifiableSet(warps.keySet()); } // Kit management public void addKit(String name, Kit kit) { kits.put(name.toLowerCase(), kit); } public Kit getKit(String name) { return kits.get(name.toLowerCase()); } public boolean removeKit(String name) { return kits.remove(name.toLowerCase()) != null; } // Player visibility public void hidePlayer(UUID playerId) { hiddenPlayers.add(playerId); } public void showPlayer(UUID playerId) { hiddenPlayers.remove(playerId); } public boolean isPlayerHidden(UUID playerId) { return hiddenPlayers.contains(playerId); } private void loadWarps() { // Load warps from configuration // Implementation depends on storage method } private void loadKits() { // Load kits from configuration // Implementation depends on storage method } } Event Handling System
1. Comprehensive Event Listeners
package com.myplugin.listeners; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.*; import org.bukkit.event.entity.*; import org.bukkit.event.block.*; import org.bukkit.event.world.*; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.ChatColor; public class PlayerListener implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); // Custom join message event.setJoinMessage(ChatColor.GREEN + "Welcome " + player.getName() + " to the server!"); // Send welcome title player.sendTitle( ChatColor.AQUA + "Welcome!", ChatColor.YELLOW + "Enjoy your stay!", 10, 70, 20 ); // Give welcome items if first join if (!player.hasPlayedBefore()) { giveWelcomeKit(player); } // Update player stats updatePlayerStats(player); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); // Custom quit message event.setQuitMessage(ChatColor.RED + "Goodbye " + player.getName() + "!"); // Save player data savePlayerData(player); } @EventHandler public void onPlayerChat(AsyncPlayerChatEvent event) { Player player = event.getPlayer(); String message = event.getMessage(); // Format chat message String format = ChatColor.GRAY + "[%s] " + ChatColor.WHITE + "%s"; event.setFormat(format); // Check for blocked words if (containsBlockedWords(message)) { event.setCancelled(true); player.sendMessage(ChatColor.RED + "Your message contains blocked words!"); return; } // Log chat messages logChatMessage(player, message); } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); // Check if player is in a restricted area if (isInRestrictedArea(player)) { event.setCancelled(true); player.sendMessage(ChatColor.RED + "You cannot enter this area!"); return; } // Update player location for tracking updatePlayerLocation(player); } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); // Custom block interactions switch (event.getAction()) { case RIGHT_CLICK_BLOCK: handleRightClick(player, event.getClickedBlock()); break; case LEFT_CLICK_BLOCK: handleLeftClick(player, event.getClickedBlock()); break; } } @EventHandler public void onPlayerDeath(PlayerDeathEvent event) { Player player = event.getEntity(); // Custom death message event.setDeathMessage(ChatColor.DARK_RED + player.getName() + " met their demise!"); // Handle death effects handleDeathEffects(player); } @EventHandler public void onPlayerRespawn(PlayerRespawnEvent event) { Player player = event.getPlayer(); // Custom respawn location if (hasCustomRespawnPoint(player)) { event.setRespawnLocation(getCustomRespawnPoint(player)); } // Give respawn items giveRespawnItems(player); } @EventHandler public void onPlayerTeleport(PlayerTeleportEvent event) { Player player = event.getPlayer(); // Log teleportation logTeleport(player, event.getFrom(), event.getTo()); // Check teleport permissions if (!canTeleport(player, event.getTo())) { event.setCancelled(true); player.sendMessage(ChatColor.RED + "You cannot teleport to this location!"); } } // Helper methods private void giveWelcomeKit(Player player) { // Implementation for giving welcome items } private void updatePlayerStats(Player player) { // Update player statistics } private void savePlayerData(Player player) { // Save player data to file/database } private boolean containsBlockedWords(String message) { // Check for inappropriate content return false; // Implementation needed } private void logChatMessage(Player player, String message) { // Log chat message for moderation } private boolean isInRestrictedArea(Player player) { // Check if player is in restricted area return false; // Implementation needed } private void updatePlayerLocation(Player player) { // Update player location for tracking } private void handleRightClick(Player player, org.bukkit.block.Block block) { // Handle right-click interactions } private void handleLeftClick(Player player, org.bukkit.block.Block block) { // Handle left-click interactions } private void handleDeathEffects(Player player) { // Handle death effects like sound, particles, etc. } private boolean hasCustomRespawnPoint(Player player) { // Check if player has custom respawn point return false; // Implementation needed } private org.bukkit.Location getCustomRespawnPoint(Player player) { // Get custom respawn point return player.getWorld().getSpawnLocation(); // Default } private void giveRespawnItems(Player player) { // Give items on respawn } private void logTeleport(Player player, org.bukkit.Location from, org.bukkit.Location to) { // Log teleport for admin purposes } private boolean canTeleport(Player player, org.bukkit.Location to) { // Check teleport permissions return true; // Implementation needed } } 2. World and Entity Listeners
package com.myplugin.listeners; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.*; import org.bukkit.event.entity.*; import org.bukkit.entity.*; import org.bukkit.ChatColor; public class WorldListener implements Listener { @EventHandler public void onEntitySpawn(EntitySpawnEvent event) { Entity entity = event.getEntity(); // Limit mob spawning in certain areas if (isInProtectedArea(entity.getLocation())) { event.setCancelled(true); return; } // Custom mob spawning logic if (entity instanceof Monster) { handleMonsterSpawning((Monster) entity); } } @EventHandler public void onCreatureSpawn(CreatureSpawnEvent event) { Creature creature = event.getEntity(); // Modify spawn rates based on conditions if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NATURAL) { if (shouldReduceSpawning(creature)) { event.setCancelled(true); } } } @EventHandler public void onEntityDamage(EntityDamageEvent event) { Entity entity = event.getEntity(); // Protect certain entities from damage if (isProtectedEntity(entity)) { event.setCancelled(true); return; } // Modify damage based on conditions if (entity instanceof Player) { handlePlayerDamage((Player) entity, event); } } @EventHandler public void onEntityDeath(EntityDeathEvent event) { Entity entity = event.getEntity(); // Custom death drops if (entity instanceof Monster) { handleMonsterDeath((Monster) entity, event); } // Experience modification if (shouldModifyExperience(entity)) { event.setDroppedExp(calculateModifiedExperience(event.getDroppedExp())); } } @EventHandler public void onBlockBreak(BlockBreakEvent event) { Player player = event.getPlayer(); // Check build permissions if (!canBuild(player, event.getBlock().getLocation())) { event.setCancelled(true); player.sendMessage(ChatColor.RED + "You cannot break blocks here!"); return; } // Custom block break effects handleBlockBreakEffects(event); // Log block breaking for rollback systems logBlockBreak(player, event.getBlock()); } @EventHandler public void onBlockPlace(BlockPlaceEvent event) { Player player = event.getPlayer(); // Check build permissions if (!canBuild(player, event.getBlock().getLocation())) { event.setCancelled(true); player.sendMessage(ChatColor.RED + "You cannot place blocks here!"); return; } // Custom block placement logic handleBlockPlacement(event); } @EventHandler public void onWorldLoad(WorldLoadEvent event) { org.bukkit.World world = event.getWorld(); // Apply world-specific settings applyWorldSettings(world); } @EventHandler public void onWorldSave(WorldSaveEvent event) { org.bukkit.World world = event.getWorld(); // Perform custom save operations performCustomSaveOperations(world); } // Helper methods private boolean isInProtectedArea(org.bukkit.Location location) { // Check if location is in protected area return false; // Implementation needed } private void handleMonsterSpawning(Monster monster) { // Custom monster spawning logic } private boolean shouldReduceSpawning(Creature creature) { // Determine if spawning should be reduced return false; // Implementation needed } private boolean isProtectedEntity(Entity entity) { // Check if entity is protected return false; // Implementation needed } private void handlePlayerDamage(Player player, EntityDamageEvent event) { // Custom player damage handling } private void handleMonsterDeath(Monster monster, EntityDeathEvent event) { // Custom monster death handling } private boolean shouldModifyExperience(Entity entity) { // Check if experience should be modified return false; // Implementation needed } private int calculateModifiedExperience(int originalExp) { // Calculate modified experience return originalExp; // Implementation needed } private boolean canBuild(Player player, org.bukkit.Location location) { // Check build permissions return true; // Implementation needed } private void handleBlockBreakEffects(BlockBreakEvent event) { // Custom block break effects } private void logBlockBreak(Player player, org.bukkit.block.Block block) { // Log block breaking } private void handleBlockPlacement(BlockPlaceEvent event) { // Custom block placement handling } private void applyWorldSettings(org.bukkit.World world) { // Apply custom world settings } private void performCustomSaveOperations(org.bukkit.World world) { // Perform custom save operations } } Command System Implementation
1. Command Executors
package com.myplugin.commands; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.ChatColor; import java.util.*; public class HelloCommand implements CommandExecutor, TabCompleter { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(ChatColor.RED + "This command can only be used by players!"); return true; } Player player = (Player) sender; if (args.length == 0) { // Basic hello command player.sendMessage(ChatColor.GREEN + "Hello, " + player.getName() + "!"); player.sendMessage(ChatColor.YELLOW + "Welcome to the server!"); // Play sound effect player.playSound(player.getLocation(), org.bukkit.Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f); // Spawn particles player.spawnParticle(org.bukkit.Particle.HEART, player.getLocation().add(0, 2, 0), 10); } else if (args.length == 1) { // Personalized hello String targetName = args[0]; Player target = player.getServer().getPlayer(targetName); if (target != null && target.isOnline()) { player.sendMessage(ChatColor.GREEN + "You said hello to " + target.getName() + "!"); target.sendMessage(ChatColor.YELLOW + player.getName() + " says hello to you!"); } else { player.sendMessage(ChatColor.RED + "Player not found or offline!"); } } else { player.sendMessage(ChatColor.RED + "Usage: /hello [player]"); } return true; } @Override public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { List<String> completions = new ArrayList<>(); if (args.length == 1) { // Tab complete player names for (Player player : sender.getServer().getOnlinePlayers()) { if (player.getName().toLowerCase().startsWith(args[0].toLowerCase())) { completions.add(player.getName()); } } } return completions; } } public class WarpCommand implements CommandExecutor, TabCompleter { private final com.myplugin.MyAwesomePlugin plugin; public WarpCommand() { this.plugin = com.myplugin.MyAwesomePlugin.getInstance(); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(ChatColor.RED + "This command can only be used by players!"); return true; } Player player = (Player) sender; if (args.length == 0) { // List available warps Set<String> warpNames = plugin.getPluginManager().getWarpNames(); if (warpNames.isEmpty()) { player.sendMessage(ChatColor.YELLOW + "No warps available."); } else { player.sendMessage(ChatColor.GREEN + "Available warps:"); for (String warpName : warpNames) { player.sendMessage(ChatColor.WHITE + " - " + warpName); } } return true; } String warpName = args[0].toLowerCase(); Warp warp = plugin.getPluginManager().getWarp(warpName); if (warp == null) { player.sendMessage(ChatColor.RED + "Warp '" + warpName + "' not found!"); return true; } // Check permissions if (!player.hasPermission("myplugin.warp." + warpName) && !player.hasPermission("myplugin.warp.*")) { player.sendMessage(ChatColor.RED + "You don't have permission to use this warp!"); return true; } // Teleport player with effects player.sendMessage(ChatColor.GREEN + "Teleporting to " + warpName + "..."); // Delay teleport for effect plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { if (player.isOnline()) { player.teleport(warp.getLocation()); player.sendMessage(ChatColor.GREEN + "Welcome to " + warpName + "!"); player.playSound(player.getLocation(), org.bukkit.Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 1.0f); } }, 20L); // 1 second delay return true; } @Override public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { List<String> completions = new ArrayList<>(); if (args.length == 1) { // Tab complete warp names Set<String> warpNames = plugin.getPluginManager().getWarpNames(); for (String warpName : warpNames) { if (warpName.toLowerCase().startsWith(args[0].toLowerCase())) { completions.add(warpName); } } } return completions; } } public class KitCommand implements CommandExecutor, TabCompleter { private final com.myplugin.MyAwesomePlugin plugin; public KitCommand() { this.plugin = com.myplugin.MyAwesomePlugin.getInstance(); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(ChatColor.RED + "This command can only be used by players!"); return true; } Player player = (Player) sender; if (args.length == 0) { // List available kits // Implementation similar to warp list return true; } String kitName = args[0].toLowerCase(); Kit kit = plugin.getPluginManager().getKit(kitName); if (kit == null) { player.sendMessage(ChatColor.RED + "Kit '" + kitName + "' not found!"); return true; } // Check cooldown if (kit.hasCooldown(player)) { player.sendMessage(ChatColor.RED + "This kit is on cooldown!"); return true; } // Give kit items kit.giveToPlayer(player); player.sendMessage(ChatColor.GREEN + "You received the " + kitName + " kit!"); // Set cooldown kit.setCooldown(player); return true; } @Override public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { // Implementation similar to WarpCommand return new ArrayList<>(); } } 2. Admin Command with Subcommands
package com.myplugin.commands; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.ChatColor; import java.util.*; public class MainCommand implements CommandExecutor, TabCompleter { private final com.myplugin.MyAwesomePlugin plugin; public MainCommand() { this.plugin = com.myplugin.MyAwesomePlugin.getInstance(); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (args.length == 0) { sendHelp(sender); return true; } String subCommand = args[0].toLowerCase(); switch (subCommand) { case "reload": return reloadCommand(sender); case "save": return saveCommand(sender); case "info": return infoCommand(sender); case "setwarp": return setWarpCommand(sender, args); case "delwarp": return delWarpCommand(sender, args); default: sender.sendMessage(ChatColor.RED + "Unknown subcommand!"); sendHelp(sender); return true; } } private boolean reloadCommand(CommandSender sender) { if (!sender.hasPermission("myplugin.admin")) { sender.sendMessage(ChatColor.RED + "You don't have permission to use this command!"); return true; } plugin.reloadConfig(); plugin.getPluginManager().initialize(); sender.sendMessage(ChatColor.GREEN + "Plugin configuration reloaded!"); return true; } private boolean saveCommand(CommandSender sender) { if (!sender.hasPermission("myplugin.admin")) { sender.sendMessage(ChatColor.RED + "You don't have permission to use this command!"); return true; } plugin.getDataManager().saveAll(); sender.sendMessage(ChatColor.GREEN + "All data saved!"); return true; } private boolean infoCommand(CommandSender sender) { sender.sendMessage(ChatColor.GOLD + "=== MyAwesomePlugin Information ==="); sender.sendMessage(ChatColor.YELLOW + "Version: " + plugin.getDescription().getVersion()); sender.sendMessage(ChatColor.YELLOW + "Author: " + plugin.getDescription().getAuthors()); sender.sendMessage(ChatColor.YELLOW + "Warps: " + plugin.getPluginManager().getWarpNames().size()); sender.sendMessage(ChatColor.YELLOW + "Kits: " + "Implementation needed"); // Add kit count return true; } private boolean setWarpCommand(CommandSender sender, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(ChatColor.RED + "This command can only be used by players!"); return true; } if (!sender.hasPermission("myplugin.admin")) { sender.sendMessage(ChatColor.RED + "You don't have permission to use this command!"); return true; } if (args.length < 2) { sender.sendMessage(ChatColor.RED + "Usage: /myplugin setwarp <name>"); return true; } Player player = (Player) sender; String warpName = args[1].toLowerCase(); Warp warp = new Warp(warpName, player.getLocation()); plugin.getPluginManager().addWarp(warpName, warp); player.sendMessage(ChatColor.GREEN + "Warp '" + warpName + "' set at your location!"); return true; } private boolean delWarpCommand(CommandSender sender, String[] args) { if (!sender.hasPermission("myplugin.admin")) { sender.sendMessage(ChatColor.RED + "You don't have permission to use this command!"); return true; } if (args.length < 2) { sender.sendMessage(ChatColor.RED + "Usage: /myplugin delwarp <name>"); return true; } String warpName = args[1].toLowerCase(); if (plugin.getPluginManager().removeWarp(warpName)) { sender.sendMessage(ChatColor.GREEN + "Warp '" + warpName + "' deleted!"); } else { sender.sendMessage(ChatColor.RED + "Warp '" + warpName + "' not found!"); } return true; } private void sendHelp(CommandSender sender) { sender.sendMessage(ChatColor.GOLD + "=== MyAwesomePlugin Commands ==="); sender.sendMessage(ChatColor.YELLOW + "/myplugin reload - Reload plugin configuration"); sender.sendMessage(ChatColor.YELLOW + "/myplugin save - Save all plugin data"); sender.sendMessage(ChatColor.YELLOW + "/myplugin info - Show plugin information"); if (sender.hasPermission("myplugin.admin")) { sender.sendMessage(ChatColor.YELLOW + "/myplugin setwarp <name> - Set a warp"); sender.sendMessage(ChatColor.YELLOW + "/myplugin delwarp <name> - Delete a warp"); } } @Override public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { List<String> completions = new ArrayList<>(); if (args.length == 1) { String[] subCommands = {"reload", "save", "info"}; for (String subCommand : subCommands) { if (subCommand.startsWith(args[0].toLowerCase())) { completions.add(subCommand); } } if (sender.hasPermission("myplugin.admin")) { String[] adminCommands = {"setwarp", "delwarp"}; for (String adminCommand : adminCommands) { if (adminCommand.startsWith(args[0].toLowerCase())) { completions.add(adminCommand); } } } } else if (args.length == 2) { if (args[0].equalsIgnoreCase("delwarp") && sender.hasPermission("myplugin.admin")) { // Tab complete warp names for deletion Set<String> warpNames = plugin.getPluginManager().getWarpNames(); for (String warpName : warpNames) { if (warpName.toLowerCase().startsWith(args[1].toLowerCase())) { completions.add(warpName); } } } } return completions; } } Data Management System
1. Configuration and Data Manager
```java
package com.myplugin;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.; import java.util.;
public class DataManager {
private final JavaPlugin plugin;
private FileConfiguration dataConfig;
private File dataFile;
public DataManager(JavaPlugin plugin) { this.plugin = plugin; this.dataFile = new File(plugin.getDataFolder(), "data.yml"); } public void initialize() { if (!plugin.getDataFolder().exists()) { plugin.getDataFolder().mkdirs(); } if (!dataFile.exists()) { try { dataFile.createNewFile(); } catch (IOException e) { plugin.getLogger().severe("Could not create data.yml file!"); e.printStackTrace(); } } dataConfig = YamlConfiguration.loadConfiguration(dataFile); } public void saveAll() { try { dataConfig.save(dataFile); plugin.getLogger().info("Data saved successfully!"); } catch (IOException e) { plugin.getLogger().severe("Could not save data.yml file!"); e.printStackTrace(); } } // Warp data management public void saveWarp(String name, Warp warp) { String path = "warps." + name; dataConfig.set(path + ".world", warp.getLocation().getWorld().getName()); dataConfig.set(path + ".x", warp.getLocation().getX()); dataConfig.set(path + ".y", warp.getLocation().getY()); dataConfig.set(path + ".z", warp.getLocation().getZ()); dataConfig.set(path + ".yaw", warp.getLocation().getYaw()); dataConfig.set(path + ".pitch", warp.getLocation().getPitch()); dataConfig.set(path + ".creator", warp.getCreator()); dataConfig.set(path + ".created", warp.getCreatedTimestamp()); } public Warp loadWarp(String name) { String path = "warps." + name; if (!dataConfig.contains(path)) { return null; } String worldName = dataConfig.getString(path + ".world"); double x = dataConfig.getDouble(path + ".x"); double y = dataConfig.getDouble(path + ".y"); double z = dataConfig.getDouble(path + ".z"); float yaw = (float) dataConfig.getDouble(path + ".yaw"); float pitch = (float) dataConfig.getDouble(path + ".pitch"); String creator = dataConfig.getString(path + ".creator"); long created = dataConfig.getLong(path + ".created"); org.bukkit.World world = plugin.getServer().getWorld(worldName); if (world == null) { plugin.getLogger().warning("World not found for warp: " + name); return null; } org.bukkit.Location location = new org.b