package popa; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.java.JavaPlugin; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.net.InetAddress; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import com.google.gson.JsonObject; import com.google.gson.JsonArray; import com.google.gson.JsonParser; import com.google.gson.JsonElement; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.Material; import com.google.gson.Gson; public final class popa extends JavaPlugin implements Listener { private HttpClient httpClient; private String apiUrl; private String serverIp; private String commandsUrl; private String inventoryRequestsUrl; private String marketplaceUrl; private final Map playerLoginTimes = new HashMap<>(); private final Map playerNames = new HashMap<>(); private ScheduledExecutorService scheduler; private Gson gson; @Override public void onEnable() { // Создаем или загружаем конфиг saveDefaultConfig(); reloadConfig(); // Получаем настройки из конфига String apiBase = getConfig().getString("api-base", "http://localhost:8000"); apiUrl = getConfig().getString("api-url", apiBase + "/api/server/events"); commandsUrl = getConfig().getString("commands-url", apiBase + "/api/server/commands"); inventoryRequestsUrl = getConfig().getString("inventory-requests-url", apiBase + "/api/server/inventory/requests"); serverIp = getConfig().getString("server-ip", getServerIp()); // Инициализируем HTTP клиент httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1) .connectTimeout(Duration.ofSeconds(10)) .build(); // Инициализируем Gson для работы с JSON gson = new Gson(); getServer().getPluginManager().registerEvents(this, this); // Запускаем планировщик для проверки команд scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(this::checkAndExecuteCommands, 5, 5, TimeUnit.SECONDS); // Запускаем планировщик для проверки запросов инвентаря scheduler.scheduleAtFixedRate(this::checkInventoryRequests, 5, 5, TimeUnit.SECONDS); // Отправка текущих онлайн-игроков при старте плагина scheduler.scheduleAtFixedRate(this::sendOnlinePlayersUpdate, 60, 60, TimeUnit.SECONDS); marketplaceUrl = getConfig().getString("api-base", "http://localhost:8000") + "/api/marketplace"; scheduler.scheduleAtFixedRate(this::checkMarketplaceOperations, 3, 3, TimeUnit.SECONDS); getLogger().info("PopaPlugin has been enabled!"); getLogger().info("API URL: " + apiUrl); getLogger().info("Commands URL: " + commandsUrl); getLogger().info("Inventory Requests URL: " + inventoryRequestsUrl); getLogger().info("Server IP: " + serverIp); } @Override public void onDisable() { // Отправка финального списка игроков при выключении sendOnlinePlayersUpdate(); // Останавливаем планировщик if (scheduler != null) { scheduler.shutdown(); try { if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } } getLogger().info("PopaPlugin has been disabled!"); } private String getServerIp() { try { return InetAddress.getLocalHost().getHostAddress(); } catch (Exception e) { return "unknown"; } } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); String playerName = player.getName(); playerLoginTimes.put(playerId, System.currentTimeMillis()); playerNames.put(playerId, playerName); String joinPayload = String.format( "{\"event_type\":\"player_join\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\"}", playerId, playerName, serverIp ); sendEventToBackend(joinPayload); sendOnlinePlayersUpdate(); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); String playerName = player.getName(); if (playerLoginTimes.containsKey(playerId)) { long sessionTime = (System.currentTimeMillis() - playerLoginTimes.get(playerId)) / 1000; String sessionPayload = String.format( "{\"event_type\":\"player_session\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"duration\":%d,\"server_ip\":\"%s\"}", playerId, playerName, sessionTime, serverIp ); sendEventToBackend(sessionPayload); } String quitPayload = String.format( "{\"event_type\":\"player_quit\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\"}", playerId, playerName, serverIp ); sendEventToBackend(quitPayload); playerLoginTimes.remove(playerId); playerNames.remove(playerId); sendOnlinePlayersUpdate(); } private void sendOnlinePlayersUpdate() { StringBuilder playersJson = new StringBuilder("["); boolean first = true; for (Map.Entry entry : playerLoginTimes.entrySet()) { UUID playerId = entry.getKey(); String playerName = playerNames.get(playerId); long onlineTime = (System.currentTimeMillis() - entry.getValue()) / 1000; if (!first) playersJson.append(","); first = false; playersJson.append(String.format( "{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d}", playerId, playerName, onlineTime )); } playersJson.append("]"); String payload = String.format( "{\"event_type\":\"online_players_update\",\"players\":%s,\"timestamp\":%d,\"server_ip\":\"%s\"}", playersJson.toString(), System.currentTimeMillis(), serverIp ); sendEventToBackend(payload); } private void sendEventToBackend(String jsonPayload) { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(apiUrl)) .version(HttpClient.Version.HTTP_1_1) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) .build(); try { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); getLogger().info("Event sent to backend. Response: " + response.statusCode()); } catch (IOException | InterruptedException e) { getLogger().warning("Failed to send event to backend: " + e.getMessage()); } } private void checkAndExecuteCommands() { if (!getServer().isPrimaryThread()) { Bukkit.getScheduler().runTask(this, this::checkAndExecuteCommands); return; } try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(commandsUrl + "?server_ip=" + serverIp)) .version(HttpClient.Version.HTTP_1_1) .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); JsonArray commands = json.getAsJsonArray("commands"); for (JsonElement cmd : commands) { JsonObject command = cmd.getAsJsonObject(); String cmdText = command.get("command").getAsString(); boolean requireOnline = command.get("require_online_player").getAsBoolean(); // Получаем оба типа сообщений String targetMessage = null; String globalMessage = null; if (command.has("target_message") && !command.get("target_message").isJsonNull()) { targetMessage = command.get("target_message").getAsString(); } if (command.has("global_message") && !command.get("global_message").isJsonNull()) { globalMessage = command.get("global_message").getAsString(); } if (!requireOnline || !Bukkit.getOnlinePlayers().isEmpty()) { if (cmdText.startsWith("/")) { cmdText = cmdText.substring(1); } // Выполняем команду Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmdText); getLogger().info("Выполнена команда: " + cmdText); // Если есть сообщения, отображаем их if ((targetMessage != null && !targetMessage.isEmpty()) || (globalMessage != null && !globalMessage.isEmpty())) { // Извлекаем имя целевого игрока из команды String targetPlayerName = extractTargetPlayerName(cmdText); Player targetPlayer = null; if (targetPlayerName != null) { targetPlayer = Bukkit.getPlayer(targetPlayerName); } // Отправляем персональное сообщение цели if (targetPlayer != null && targetMessage != null && !targetMessage.isEmpty()) { targetPlayer.sendMessage("§c" + targetMessage); } // Отправляем сообщение всем остальным if (globalMessage != null && !globalMessage.isEmpty()) { for (Player player : Bukkit.getOnlinePlayers()) { // Если это не целевой игрок, отправляем сообщение if (targetPlayer == null || !player.equals(targetPlayer)) { player.sendMessage("§7" + globalMessage); } } } } } } } } catch (Exception e) { getLogger().warning("Ошибка при выполнении команд: " + e.getMessage()); } } /** * Проверяет наличие запросов инвентаря на бэкенде */ private void checkInventoryRequests() { if (!getServer().isPrimaryThread()) { Bukkit.getScheduler().runTask(this, this::checkInventoryRequests); return; } try { // Выводим полный URL для отладки String requestUrl = inventoryRequestsUrl + "?server_ip=" + serverIp; getLogger().info("Запрашиваем инвентарь по URL: " + requestUrl); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(requestUrl)) .version(HttpClient.Version.HTTP_1_1) .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); // Выводим код ответа и тело для отладки getLogger().info("Получен ответ от API: " + response.statusCode()); getLogger().info("Тело ответа: " + response.body()); if (response.statusCode() == 200) { JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); // Проверяем статус if (json.has("status") && "success".equals(json.get("status").getAsString())) { if (json.has("inventory_requests")) { JsonArray requests = json.getAsJsonArray("inventory_requests"); getLogger().info("Найдено запросов инвентаря: " + requests.size()); for (JsonElement req : requests) { JsonObject request_obj = req.getAsJsonObject(); String requestId = request_obj.get("id").getAsString(); String playerName = request_obj.get("player_name").getAsString(); getLogger().info("Обработка запроса инвентаря: " + requestId + " для игрока " + playerName); // Находим игрока и отправляем его инвентарь Player player = Bukkit.getPlayer(playerName); if (player != null && player.isOnline()) { getLogger().info("Игрок " + playerName + " найден, отправляем инвентарь"); sendPlayerInventory(player, requestId); } else { getLogger().info("Игрок " + playerName + " не найден или не в сети"); sendEmptyInventory(playerName, requestId); } } } else { getLogger().info("В ответе нет поля inventory_requests"); } } else { getLogger().info("Статус ответа не success: " + json.toString()); } } else { getLogger().warning("Некорректный статус ответа: " + response.statusCode()); } } catch (Exception e) { getLogger().warning("Ошибка при проверке запросов инвентаря: " + e.getMessage()); if (e.getCause() != null) { getLogger().warning("Причина: " + e.getCause().getMessage()); } e.printStackTrace(); } } /** * Отправляет данные о содержимом инвентаря игрока на бэкенд */ private void sendPlayerInventory(Player player, String requestId) { // Формируем JSON с содержимым инвентаря PlayerInventory inv = player.getInventory(); JsonArray inventoryArray = new JsonArray(); // Добавляем основные слоты (0-35) for (int i = 0; i < 36; i++) { ItemStack item = inv.getItem(i); inventoryArray.add(itemStackToJson(item, i)); } // Добавляем слот шлема (39) inventoryArray.add(itemStackToJson(inv.getHelmet(), 39)); // Добавляем слот нагрудника (38) inventoryArray.add(itemStackToJson(inv.getChestplate(), 38)); // Добавляем слот поножей (37) inventoryArray.add(itemStackToJson(inv.getLeggings(), 37)); // Добавляем слот ботинок (36) inventoryArray.add(itemStackToJson(inv.getBoots(), 36)); // Добавляем слот дополнительной руки (40) если это доступно inventoryArray.add(itemStackToJson(inv.getItemInOffHand(), 40)); // Формируем объект ответа JsonObject responseJson = new JsonObject(); responseJson.addProperty("request_id", requestId); responseJson.add("inventory", inventoryArray); responseJson.addProperty("player_name", player.getName()); responseJson.addProperty("player_id", player.getUniqueId().toString()); responseJson.addProperty("server_ip", serverIp); // Отправляем данные на бэкенд sendInventoryToBackend(responseJson.toString()); } /** * Преобразует ItemStack в JsonObject */ private JsonObject itemStackToJson(ItemStack item, int slot) { JsonObject itemJson = new JsonObject(); itemJson.addProperty("slot", slot); if (item == null || item.getType() == Material.AIR) { itemJson.addProperty("material", "AIR"); itemJson.addProperty("amount", 0); } else { itemJson.addProperty("material", item.getType().toString()); itemJson.addProperty("amount", item.getAmount()); if (item.hasItemMeta()) { if (item.getItemMeta().hasDisplayName()) { itemJson.addProperty("display_name", item.getItemMeta().getDisplayName()); } if (item.getItemMeta().hasLore()) { JsonArray loreArray = new JsonArray(); for (String line : item.getItemMeta().getLore()) { loreArray.add(line); } itemJson.add("lore", loreArray); } if (item.getItemMeta().hasEnchants()) { JsonObject enchants = new JsonObject(); item.getItemMeta().getEnchants().forEach((enchantment, level) -> { enchants.addProperty(enchantment.getKey().toString(), level); }); itemJson.add("enchants", enchants); } } } return itemJson; } /** * Отправляет информацию о пустом инвентаре для оффлайн-игрока */ private void sendEmptyInventory(String playerName, String requestId) { JsonObject responseJson = new JsonObject(); responseJson.addProperty("request_id", requestId); responseJson.add("inventory", new JsonArray()); responseJson.addProperty("player_name", playerName); responseJson.addProperty("server_ip", serverIp); responseJson.addProperty("status", "player_offline"); sendInventoryToBackend(responseJson.toString()); } /** * Отправляет данные инвентаря на бэкенд */ private void sendInventoryToBackend(String jsonPayload) { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(getConfig().getString("api-base", "http://localhost:8000") + "/api/server/inventory/submit")) .version(HttpClient.Version.HTTP_1_1) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) .build(); try { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); getLogger().info("Inventory data sent to backend. Response: " + response.statusCode()); } catch (IOException | InterruptedException e) { getLogger().warning("Failed to send inventory data to backend: " + e.getMessage()); } } /** * Проверяет запросы на операции с торговой площадкой */ private void checkMarketplaceOperations() { if (!getServer().isPrimaryThread()) { Bukkit.getScheduler().runTask(this, this::checkMarketplaceOperations); return; } try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(marketplaceUrl + "/operations?server_ip=" + serverIp)) .version(HttpClient.Version.HTTP_1_1) .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); if (json.has("operations") && json.get("operations").isJsonArray()) { JsonArray operations = json.getAsJsonArray("operations"); for (JsonElement op : operations) { JsonObject operation = op.getAsJsonObject(); String type = operation.get("type").getAsString(); String opId = operation.get("id").getAsString(); if ("sell".equals(type)) { handleSellOperation(operation); } else if ("buy".equals(type)) { handleBuyOperation(operation); } // Подтверждаем выполнение операции confirmOperation(opId); } } } } catch (Exception e) { getLogger().warning("Ошибка при проверке операций торговой площадки: " + e.getMessage()); e.printStackTrace(); } } /** * Обрабатывает операцию продажи предмета */ private void handleSellOperation(JsonObject operation) { String playerName = operation.get("player_name").getAsString(); int slotIndex = operation.get("slot_index").getAsInt(); Player player = Bukkit.getPlayer(playerName); if (player == null || !player.isOnline()) { sendOperationError(operation.get("id").getAsString(), "Игрок не в сети"); return; } PlayerInventory inv = player.getInventory(); ItemStack item = null; // Получаем предмет из нужного слота if (slotIndex >= 0 && slotIndex <= 35) { item = inv.getItem(slotIndex); } else if (slotIndex == 36) { item = inv.getBoots(); } else if (slotIndex == 37) { item = inv.getLeggings(); } else if (slotIndex == 38) { item = inv.getChestplate(); } else if (slotIndex == 39) { item = inv.getHelmet(); } else if (slotIndex == 40) { item = inv.getItemInOffHand(); } if (item == null || item.getType() == Material.AIR) { sendOperationError(operation.get("id").getAsString(), "Предмет не найден в указанном слоте"); return; } // Создаем полную копию предмета с метаданными для отправки на сервер JsonObject itemData = itemStackToDetailedJson(item, slotIndex); // Очищаем слот if (slotIndex >= 0 && slotIndex <= 35) { inv.setItem(slotIndex, null); } else if (slotIndex == 36) { inv.setBoots(null); } else if (slotIndex == 37) { inv.setLeggings(null); } else if (slotIndex == 38) { inv.setChestplate(null); } else if (slotIndex == 39) { inv.setHelmet(null); } else if (slotIndex == 40) { inv.setItemInOffHand(null); } // Отправляем сообщение игроку int price = operation.get("price").getAsInt(); player.sendMessage("§aВы выставили предмет на продажу за " + price + " монет"); // Отправляем данные о предмете на сервер sendItemDetails(operation.get("id").getAsString(), itemData); } /** * Обрабатывает операцию покупки предмета */ private void handleBuyOperation(JsonObject operation) { String playerName = operation.get("player_name").getAsString(); JsonObject itemData = operation.getAsJsonObject("item_data"); Player player = Bukkit.getPlayer(playerName); if (player == null || !player.isOnline()) { sendOperationError(operation.get("id").getAsString(), "Игрок не в сети"); return; } // Создаем предмет из данных ItemStack item = jsonToItemStack(itemData); if (item == null) { sendOperationError(operation.get("id").getAsString(), "Не удалось создать предмет"); return; } // Добавляем предмет в инвентарь HashMap leftItems = player.getInventory().addItem(item); // Если предмет не поместился полностью, бросаем остаток на землю if (!leftItems.isEmpty()) { for (ItemStack leftItem : leftItems.values()) { player.getWorld().dropItem(player.getLocation(), leftItem); } } // Отправляем сообщение игроку int price = operation.get("price").getAsInt(); player.sendMessage("§aВы купили предмет за " + price + " монет"); } /** * Создает ItemStack из JSON данных */ private ItemStack jsonToItemStack(JsonObject itemData) { try { String materialName = itemData.get("material").getAsString(); Material material = Material.valueOf(materialName); int amount = itemData.get("amount").getAsInt(); ItemStack item = new ItemStack(material, amount); // Если есть мета-данные if (itemData.has("meta") && !itemData.get("meta").isJsonNull()) { JsonObject meta = itemData.getAsJsonObject("meta"); org.bukkit.inventory.meta.ItemMeta itemMeta = item.getItemMeta(); // Устанавливаем отображаемое имя if (meta.has("display_name")) { itemMeta.setDisplayName(meta.get("display_name").getAsString()); } // Устанавливаем lore if (meta.has("lore") && meta.get("lore").isJsonArray()) { JsonArray loreArray = meta.getAsJsonArray("lore"); java.util.List lore = new java.util.ArrayList<>(); for (JsonElement line : loreArray) { lore.add(line.getAsString()); } itemMeta.setLore(lore); } // Устанавливаем зачарования if (meta.has("enchants") && meta.get("enchants").isJsonObject()) { JsonObject enchants = meta.getAsJsonObject("enchants"); for (Map.Entry entry : enchants.entrySet()) { org.bukkit.enchantments.Enchantment enchantment = org.bukkit.enchantments.Enchantment.getByKey(org.bukkit.NamespacedKey.minecraft(entry.getKey())); if (enchantment != null) { itemMeta.addEnchant(enchantment, entry.getValue().getAsInt(), true); } } } // Устанавливаем прочность if (meta.has("durability") && itemMeta instanceof org.bukkit.inventory.meta.Damageable) { org.bukkit.inventory.meta.Damageable damageable = (org.bukkit.inventory.meta.Damageable) itemMeta; damageable.setDamage(meta.get("durability").getAsInt()); } item.setItemMeta(itemMeta); } return item; } catch (Exception e) { getLogger().warning("Ошибка при создании предмета из JSON: " + e.getMessage()); e.printStackTrace(); return null; } } /** * Преобразует ItemStack в детальный JsonObject со всеми мета-данными */ private JsonObject itemStackToDetailedJson(ItemStack item, int slot) { JsonObject itemJson = new JsonObject(); itemJson.addProperty("slot", slot); if (item == null || item.getType() == Material.AIR) { itemJson.addProperty("material", "AIR"); itemJson.addProperty("amount", 0); return itemJson; } itemJson.addProperty("material", item.getType().toString()); itemJson.addProperty("amount", item.getAmount()); // Добавляем мета-данные if (item.hasItemMeta()) { JsonObject metaJson = new JsonObject(); if (item.getItemMeta().hasDisplayName()) { metaJson.addProperty("display_name", item.getItemMeta().getDisplayName()); } if (item.getItemMeta().hasLore()) { JsonArray loreArray = new JsonArray(); for (String line : item.getItemMeta().getLore()) { loreArray.add(line); } metaJson.add("lore", loreArray); } if (item.getItemMeta().hasEnchants()) { JsonObject enchants = new JsonObject(); item.getItemMeta().getEnchants().forEach((enchantment, level) -> { enchants.addProperty(enchantment.getKey().getKey(), level); }); metaJson.add("enchants", enchants); } // Сохраняем данные о прочности if (item.getItemMeta() instanceof org.bukkit.inventory.meta.Damageable) { org.bukkit.inventory.meta.Damageable damageable = (org.bukkit.inventory.meta.Damageable) item.getItemMeta(); metaJson.addProperty("durability", damageable.getDamage()); } itemJson.add("meta", metaJson); } return itemJson; } /** * Отправляет детальную информацию о предмете на сервер */ private void sendItemDetails(String operationId, JsonObject itemData) { JsonObject payload = new JsonObject(); payload.addProperty("operation_id", operationId); payload.add("item_data", itemData); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(marketplaceUrl + "/items/details")) .version(HttpClient.Version.HTTP_1_1) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(payload.toString())) .build(); try { httpClient.send(request, HttpResponse.BodyHandlers.ofString()); } catch (Exception e) { getLogger().warning("Ошибка при отправке данных предмета: " + e.getMessage()); } } /** * Отправляет ошибку операции */ private void sendOperationError(String operationId, String error) { JsonObject payload = new JsonObject(); payload.addProperty("operation_id", operationId); payload.addProperty("status", "error"); payload.addProperty("error", error); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(marketplaceUrl + "/operations/confirm")) .version(HttpClient.Version.HTTP_1_1) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(payload.toString())) .build(); try { httpClient.send(request, HttpResponse.BodyHandlers.ofString()); } catch (Exception e) { getLogger().warning("Ошибка при отправке ошибки операции: " + e.getMessage()); } } /** * Подтверждает выполнение операции */ private void confirmOperation(String operationId) { JsonObject payload = new JsonObject(); payload.addProperty("operation_id", operationId); payload.addProperty("status", "success"); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(marketplaceUrl + "/operations/confirm")) .version(HttpClient.Version.HTTP_1_1) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(payload.toString())) .build(); try { httpClient.send(request, HttpResponse.BodyHandlers.ofString()); } catch (Exception e) { getLogger().warning("Ошибка при подтверждении операции: " + e.getMessage()); } } /** * Пытается извлечь имя целевого игрока из команды */ private String extractTargetPlayerName(String command) { // Ищем по шаблону в команде имя игрока // Пример: в команде "execute at PlayerName run summon lightning_bolt ~ ~ ~" // нужно извлечь PlayerName String[] parts = command.split(" "); for (int i = 0; i < parts.length; i++) { if (i > 0 && parts[i-1].equalsIgnoreCase("at")) { // После слова "at" обычно идет имя игрока String potentialName = parts[i]; // Проверяем, существует ли такой игрок if (Bukkit.getPlayer(potentialName) != null) { return potentialName; } } } return null; } }