diff --git a/src/main/java/popa/popa.java b/src/main/java/popa/popa.java index 3df3a98..3da679e 100644 --- a/src/main/java/popa/popa.java +++ b/src/main/java/popa/popa.java @@ -36,6 +36,13 @@ import org.bukkit.event.player.PlayerExpChangeEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.EntityType; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; public final class popa extends JavaPlugin implements Listener { @@ -55,6 +62,9 @@ public final class popa extends JavaPlugin implements Listener { private ScheduledExecutorService scheduler; private Gson gson; private final Map lastActivityTimes = new HashMap<>(); + private final Map afkStates = new HashMap<>(); + private long afkThresholdMillis; + private String afkPrefix; @Override public void onEnable() { @@ -157,6 +167,12 @@ public final class popa extends JavaPlugin implements Listener { getLogger().info("Marketplace URL: " + marketplaceUrl); getLogger().info("Bonuses URL: " + bonusesUrl); getLogger().info("Server IP: " + serverIp); + + afkThresholdMillis = getConfig().getLong("afk-threshold-seconds", 60) * 1000L; + afkPrefix = getConfig().getString("afk-prefix", "[AFK] "); + + // Периодически проверяем, кто ушёл в AFK (на основном потоке) + Bukkit.getScheduler().runTaskTimer(this, this::checkAfkTransitions, 20L, 20L * 5L); } @Override @@ -189,14 +205,49 @@ public final class popa extends JavaPlugin implements Listener { @EventHandler public void onPlayerMove(PlayerMoveEvent event) { - // Игнорируем микродвижения (изменение направления взгляда) - if (event.getFrom().getX() != event.getTo().getX() || - event.getFrom().getY() != event.getTo().getY() || - event.getFrom().getZ() != event.getTo().getZ()) { - lastActivityTimes.put(event.getPlayer().getUniqueId(), System.currentTimeMillis()); + if (!event.getFrom().toVector().equals(event.getTo().toVector())) { + markActivity(event.getPlayer()); } } + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + // чат — асинхронный, переключаемся на основной поток + Bukkit.getScheduler().runTask(this, () -> markActivity(event.getPlayer())); + } + + @EventHandler + public void onCommand(PlayerCommandPreprocessEvent event) { + markActivity(event.getPlayer()); + } + + @EventHandler + public void onInteract(PlayerInteractEvent event) { + markActivity(event.getPlayer()); + } + + @EventHandler + public void onInvClick(InventoryClickEvent event) { + if (event.getWhoClicked() instanceof Player p) { + markActivity(p); + } + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + markActivity(event.getPlayer()); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + markActivity(event.getPlayer()); + } + + @EventHandler + public void onHotbar(PlayerItemHeldEvent event) { + markActivity(event.getPlayer()); + } + @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); @@ -217,6 +268,9 @@ public final class popa extends JavaPlugin implements Listener { // сразу обновим онлайн-лист на бэкенде sendOnlinePlayersUpdate(); + + lastActivityTimes.put(playerId, System.currentTimeMillis()); + afkStates.put(playerId, false); } @EventHandler @@ -249,6 +303,9 @@ public final class popa extends JavaPlugin implements Listener { // Восстанавливаем базовые атрибуты player.getAttribute(Attribute.MAX_HEALTH).setBaseValue(20.0); + + lastActivityTimes.remove(playerId); + afkStates.remove(playerId); } @EventHandler @@ -269,6 +326,68 @@ public final class popa extends JavaPlugin implements Listener { sendEventToBackend(payload); } + private void pushOnlineUpdateAsync() { + Bukkit.getScheduler().runTaskAsynchronously(this, this::sendOnlinePlayersUpdate); + } + + private void markActivity(Player player) { + UUID id = player.getUniqueId(); + long now = System.currentTimeMillis(); + lastActivityTimes.put(id, now); + + if (afkStates.getOrDefault(id, false)) { + afkStates.put(id, false); + player.sendMessage("Вы больше не AFK"); + + // 1) опционально: отдельное событие выхода из AFK + Bukkit.getScheduler().runTaskAsynchronously(this, () -> { + sendEventToBackend(String.format( + "{\"event_type\":\"player_afk_end\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\",\"timestamp\":%d}", + id, player.getName(), serverIp, now + )); + }); + + // 2) и сразу пушим обновление списка онлайна (чтобы бэкенд обновился мгновенно) + pushOnlineUpdateAsync(); + } + } + + private void setAfk(Player player) { + UUID id = player.getUniqueId(); + if (afkStates.getOrDefault(id, false)) return; + + afkStates.put(id, true); + player.sendMessage("Вы AFK"); + + Bukkit.getScheduler().runTaskAsynchronously(this, () -> { + sendEventToBackend(String.format( + "{\"event_type\":\"player_afk_start\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\",\"timestamp\":%d}", + id, player.getName(), serverIp, System.currentTimeMillis() + )); + }); + + pushOnlineUpdateAsync(); + } + + private void checkAfkTransitions() { + long now = System.currentTimeMillis(); + + for (Player p : Bukkit.getOnlinePlayers()) { + UUID id = p.getUniqueId(); + + // если ещё нет активности — считаем что активен “сейчас” + long last = lastActivityTimes.getOrDefault(id, now); + lastActivityTimes.putIfAbsent(id, last); + + boolean isAfk = afkStates.getOrDefault(id, false); + boolean shouldBeAfk = (now - last) > afkThresholdMillis; + + if (!isAfk && shouldBeAfk) { + setAfk(p); + } + } + } + private void sendOnlinePlayersUpdate() { StringBuilder playersJson = new StringBuilder("["); boolean first = true; @@ -278,18 +397,8 @@ public final class popa extends JavaPlugin implements Listener { UUID playerId = entry.getKey(); String playerName = playerNames.get(playerId); Long lastActivity = lastActivityTimes.get(playerId); - - // Если нет последней активности — считаем, что игрок "не виден" бэкенду - if (lastActivity == null) { - continue; - } - - long inactiveSeconds = (currentTime - lastActivity) / 1000; - - // Больше минуты не двигался — не отправляем в список активных - if (inactiveSeconds > afkTimeoutSeconds) { - continue; - } + boolean isAfk = ((currentTime - lastActivity) > afkThresholdMillis); + String nameForSite = isAfk ? (afkPrefix + playerName) : playerName; long onlineTime = (currentTime - entry.getValue()) / 1000; @@ -297,8 +406,8 @@ public final class popa extends JavaPlugin implements Listener { first = false; playersJson.append(String.format( - "{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d}", - playerId, playerName, onlineTime + "{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d,\"afk\":%s}", + playerId, nameForSite, onlineTime, isAfk ? "true" : "false" )); }