diff --git a/src/main/java/popa/popa.java b/src/main/java/popa/popa.java index fc260c8..f5f2473 100644 --- a/src/main/java/popa/popa.java +++ b/src/main/java/popa/popa.java @@ -38,6 +38,7 @@ public final class popa extends JavaPlugin implements Listener { 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; @@ -77,6 +78,9 @@ public final class popa extends JavaPlugin implements Listener { // Отправка текущих онлайн-игроков при старте плагина 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); @@ -464,6 +468,333 @@ public final class popa extends JavaPlugin implements Listener { } } + /** + * Проверяет запросы на операции с торговой площадкой + */ + 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()); + } + } + /** * Пытается извлечь имя целевого игрока из команды */ diff --git a/target/classes/popa/popa.class b/target/classes/popa/popa.class index a6f40ba..3bfeeb8 100644 Binary files a/target/classes/popa/popa.class and b/target/classes/popa/popa.class differ diff --git a/target/original-popa-1.0-SNAPSHOT.jar b/target/original-popa-1.0-SNAPSHOT.jar index e053cba..e7c8da9 100644 Binary files a/target/original-popa-1.0-SNAPSHOT.jar and b/target/original-popa-1.0-SNAPSHOT.jar differ diff --git a/target/popa-1.0-SNAPSHOT.jar b/target/popa-1.0-SNAPSHOT.jar index d8e7821..40e6e46 100644 Binary files a/target/popa-1.0-SNAPSHOT.jar and b/target/popa-1.0-SNAPSHOT.jar differ