all working
This commit is contained in:
489
src/main/java/popa/popa.java
Normal file
489
src/main/java/popa/popa.java
Normal file
@ -0,0 +1,489 @@
|
||||
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 final Map<UUID, Long> playerLoginTimes = new HashMap<>();
|
||||
private final Map<UUID, String> 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);
|
||||
|
||||
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<UUID, Long> 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<String> 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<String> 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<String> 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<String> 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 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user