Классная задача для 9–классника: сделать плагин на Java для Minecraft (Spigot/Paper), который добавляет кирку-«кирку с режимами» и даёт разные режимы работы. Ниже дам подробное решение с пояснениями и готовым примером кода.
Что делает плагин
- Добавляет специальную кирку (Mode Pickaxe) с сохранённым режимом в сам предмет.
- Режимы:
- 0 Normal (обычное поведение)
- 1 Double Drops (при добыче дропов — даются вдвое больше предметов)
- 2 Area Break (разбивает блоки в окрестности 3x3x3 вокруг целевого блока того же типа)
- Меняем режим кнопкой: зажатый Sneak (Shift) и правый клик по воздуху/блоку переключают режим на следующий.
- Можно получить кирку через команду /modepick give.
Как это работает на концептуальном уровне
- Используем PersistentDataContainer (NamespacedKey) внутри ItemStack, чтобы хранить текущий режим внутри самой кирки.
- Обработчик блок-разрушения BlockBreakEvent применяет нужное поведение в зависимости от выбранного режима.
- Обработчик взаимодействия PlayerInteractEvent позволяет перелистывать режимы.
Готовый код
- ModePickaxe.java
- Расположите файл по пути: src/main/java/com/example/modepickaxe/ModePickaxe.java
- Этот пример работает на Spigot/Paper API версии 1.13+ (используется PersistentDataContainer).
// ModePickaxe.java package com.example.modepickaxe;
import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.block.Action; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; import org.bukkit.NamespacedKey; import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays; import java.util.List;
public class ModePickaxe extends JavaPlugin implements Listener {
private NamespacedKey MODE_KEY;
@Override
public void onEnable() {
MODE_KEY = new NamespacedKey(this, "mode_pickaxe_mode");
// Регистрация обработчиков событий
getServer().getPluginManager().registerEvents(this, this);
getLogger().info("ModePickaxe включён");
}
@Override
public void onDisable() {
getLogger().info("ModePickaxe отключён");
}
// Создаём особую кирку (Mode Pickaxe)
private ItemStack createModePickaxe() {
ItemStack pick = new ItemStack(Material.IRON_PICKAXE);
ItemMeta meta = pick.getItemMeta();
meta.setDisplayName(ChatColor.GOLD + "Mode Pickaxe");
List<String> lore = Arrays.asList(
ChatColor.GRAY + "Mode: Normal",
ChatColor.GRAY + "Sneak + Right-click to cycle modes",
ChatColor.GRAY + "Modes: 0 Normal, 1 Double Drops, 2 Area Break"
);
meta.setLore(lore);
// Начальный режим = 0 (Normal)
meta.getPersistentDataContainer().set(MODE_KEY, PersistentDataType.INTEGER, 0);
pick.setItemMeta(meta);
return pick;
}
// Проверяем, является ли предмет нашей киркой
private boolean isModePickaxe(ItemStack item) {
if (item == null) return false;
if (item.getType() == Material.AIR) return false;
if (!item.hasItemMeta()) return false;
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
if (!meta.hasDisplayName()) return false;
// Название должно соответствовать нашему
if (!ChatColor.stripColor(meta.getDisplayName()).equals("Mode Pickaxe")) return false;
if (!meta.hasPersistentDataContainer()) return false;
Integer mode = meta.getPersistentDataContainer().get(MODE_KEY, PersistentDataType.INTEGER);
return mode != null;
}
// Получаем текущий режим кирки
private int getMode(ItemStack item) {
if (item != null && item.hasItemMeta()) {
ItemMeta meta = item.getItemMeta();
if (meta != null && meta.getPersistentDataContainer().has(MODE_KEY, PersistentDataType.INTEGER)) {
Integer m = meta.getPersistentDataContainer().get(MODE_KEY, PersistentDataType.INTEGER);
if (m != null) return m;
}
}
return 0; // по умолчанию Normal
}
// Устанавливаем режим кирки
private void setMode(ItemStack item, int mode) {
if (item != null && item.hasItemMeta()) {
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.getPersistentDataContainer().set(MODE_KEY, PersistentDataType.INTEGER, mode);
item.setItemMeta(meta);
}
}
}
// Переключение режимов: Sneak + Right-Click
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
Action action = event.getAction();
Player player = event.getPlayer();
ItemStack hand = player.getInventory().getItemInMainHand();
if (!isModePickaxe(hand)) return;
if (!player.isSneaking()) return;
if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) {
int mode = getMode(hand);
int next = (mode + 1) % 3;
setMode(hand, next);
String modeName = (next == 0) ? "Normal" : (next == 1) ? "Double Drops" : "Area Break";
player.sendMessage(ChatColor.GREEN + "Mode Pickaxe переключён на: " + modeName);
}
}
// Реализация поведения режимов при добыче блока
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
Block block = event.getBlock();
Player player = event.getPlayer();
ItemStack tool = player.getInventory().getItemInMainHand();
if (!isModePickaxe(tool)) return;
int mode = getMode(tool);
World world = block.getWorld();
if (mode == 1) {
// Double Drops
event.setDropItems(false);
List<ItemStack> drops = block.getDrops(tool);
Location loc = block.getLocation();
// Стандартные дропы
for (ItemStack drop : drops) {
world.dropItemNaturally(loc, drop);
}
// Удвоение дропов
for (ItemStack drop : drops) {
ItemStack d = drop.clone();
d.setAmount(drop.getAmount() * 2);
world.dropItemNaturally(loc, d);
}
} else if (mode == 2) {
// Area Break: разрушаем 3x3x3 вокруг цели, того же типа блока
event.setDropItems(false);
Material centerType = block.getType();
// Центр уже собирается как обычный разлом; добавим доп дропы и уберём блок
List<ItemStack> centerDrops = block.getDrops(tool);
for (ItemStack drop : centerDrops) {
world.dropItemNaturally(block.getLocation(), drop);
}
block.setType(Material.AIR);
// В окрестности 3x3x3
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dy == 0 && dz == 0) continue; // центр пропускаем (уже обработан)
Block b = block.getRelative(dx, dy, dz);
if (b.getType() == centerType) {
List<ItemStack> drops = b.getDrops(tool);
for (ItemStack drop : drops) {
world.dropItemNaturally(b.getLocation(), drop);
}
b.setType(Material.AIR);
}
}
}
}
}
// режим 0 (Normal) не требует изменений
}
// Обработчик команды для выдачи кирки (необязательно, но удобно для тестирования)
@Override
public boolean onCommand(org.bukkit.command.CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args) {
if (cmd.getName().equalsIgnoreCase("modepick")) {
if (!(sender instanceof Player)) {
sender.sendMessage("Эта команда доступна только игрокам.");
return true;
}
Player p = (Player) sender;
if (args.length > 0 && args[0].equalsIgnoreCase("give")) {
p.getInventory().addItem(createModePickaxe());
p.sendMessage(ChatColor.YELLOW + "Mode Pickaxe добавлена в инвентарь.");
return true;
} else {
p.sendMessage(ChatColor.GREEN + "Используйте /modepick give чтобы получить Mode Pickaxe.");
return true;
}
}
return false;
}
}
- plugin.yml
- Расположите файл в src/main/resources/plugin.yml
name: ModePickaxe version: 1.0 main: com.example.modepickaxe.ModePickaxe api-version: "1.13"
commands:
modepick:
description: Gives a Mode Pickaxe
usage: /modepick
Как использовать
- Соберите плагин в вашем проекте (Maven/Gradle) и поместите JAR в папку plugins вашего сервера Spigot/Paper.
- В консоли запустите сервер.
- В игре выполните:
- /modepick give — получить Mode Pickaxe в инвентарь.
- Наденьте кирку и зажмите Shift, правый клик по воздуху/блоку — переключение режима.
- При добыче клавишами с вашей киркой режимы будут работать как описано:
- Normal: обычное поведение.
- Double Drops: каждый добытый блок даёт вдвое больше дропа ( Python-подсказка: мы компенсируем стандартные дропы, чтобы получить суммарно 2x).
- Area Break: ломаем вокруг центр блока в радиусе 1 по всем трём осям блока того же типа.
Дополнительные заметки
- Этот пример рассчитан на версии API 1.13+ (используется PersistentDataContainer).
- Можно расширять набор режимов, добавляя новые кейсы в switch-like логику и хранить дополнительную информацию в клавишах предмета.
- При желании можно реализовать более продвинутые эффекты (например, area-режим с ограничением по прочности блока или добавлением задержек). Но для учебной цели данный пример демонстрирует работу с предметами и режимами без сложной логики майнкрафт-клиента.
Если хочешь, могу адаптировать код под конкретную версию сервера (например, 1.19 или 1.20) или добавить команду настройки по умолчанию на загрузку плагина.