This commit is contained in:
aurinex
2025-12-14 17:35:32 +05:00
parent 06ad85d6df
commit c0fe432ab0

View File

@ -36,6 +36,13 @@ import org.bukkit.event.player.PlayerExpChangeEvent;
import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.EntityType; 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 { public final class popa extends JavaPlugin implements Listener {
@ -55,6 +62,9 @@ public final class popa extends JavaPlugin implements Listener {
private ScheduledExecutorService scheduler; private ScheduledExecutorService scheduler;
private Gson gson; private Gson gson;
private final Map<UUID, Long> lastActivityTimes = new HashMap<>(); private final Map<UUID, Long> lastActivityTimes = new HashMap<>();
private final Map<UUID, Boolean> afkStates = new HashMap<>();
private long afkThresholdMillis;
private String afkPrefix;
@Override @Override
public void onEnable() { public void onEnable() {
@ -157,6 +167,12 @@ public final class popa extends JavaPlugin implements Listener {
getLogger().info("Marketplace URL: " + marketplaceUrl); getLogger().info("Marketplace URL: " + marketplaceUrl);
getLogger().info("Bonuses URL: " + bonusesUrl); getLogger().info("Bonuses URL: " + bonusesUrl);
getLogger().info("Server IP: " + serverIp); 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 @Override
@ -189,14 +205,49 @@ public final class popa extends JavaPlugin implements Listener {
@EventHandler @EventHandler
public void onPlayerMove(PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
// Игнорируем микродвижения (изменение направления взгляда) if (!event.getFrom().toVector().equals(event.getTo().toVector())) {
if (event.getFrom().getX() != event.getTo().getX() || markActivity(event.getPlayer());
event.getFrom().getY() != event.getTo().getY() ||
event.getFrom().getZ() != event.getTo().getZ()) {
lastActivityTimes.put(event.getPlayer().getUniqueId(), System.currentTimeMillis());
} }
} }
@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 @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
@ -217,6 +268,9 @@ public final class popa extends JavaPlugin implements Listener {
// сразу обновим онлайн-лист на бэкенде // сразу обновим онлайн-лист на бэкенде
sendOnlinePlayersUpdate(); sendOnlinePlayersUpdate();
lastActivityTimes.put(playerId, System.currentTimeMillis());
afkStates.put(playerId, false);
} }
@EventHandler @EventHandler
@ -249,6 +303,9 @@ public final class popa extends JavaPlugin implements Listener {
// Восстанавливаем базовые атрибуты // Восстанавливаем базовые атрибуты
player.getAttribute(Attribute.MAX_HEALTH).setBaseValue(20.0); player.getAttribute(Attribute.MAX_HEALTH).setBaseValue(20.0);
lastActivityTimes.remove(playerId);
afkStates.remove(playerId);
} }
@EventHandler @EventHandler
@ -269,6 +326,68 @@ public final class popa extends JavaPlugin implements Listener {
sendEventToBackend(payload); 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() { private void sendOnlinePlayersUpdate() {
StringBuilder playersJson = new StringBuilder("["); StringBuilder playersJson = new StringBuilder("[");
boolean first = true; boolean first = true;
@ -278,18 +397,8 @@ public final class popa extends JavaPlugin implements Listener {
UUID playerId = entry.getKey(); UUID playerId = entry.getKey();
String playerName = playerNames.get(playerId); String playerName = playerNames.get(playerId);
Long lastActivity = lastActivityTimes.get(playerId); Long lastActivity = lastActivityTimes.get(playerId);
boolean isAfk = ((currentTime - lastActivity) > afkThresholdMillis);
// Если нет последней активности — считаем, что игрок "не виден" бэкенду String nameForSite = isAfk ? (afkPrefix + playerName) : playerName;
if (lastActivity == null) {
continue;
}
long inactiveSeconds = (currentTime - lastActivity) / 1000;
// Больше минуты не двигался — не отправляем в список активных
if (inactiveSeconds > afkTimeoutSeconds) {
continue;
}
long onlineTime = (currentTime - entry.getValue()) / 1000; long onlineTime = (currentTime - entry.getValue()) / 1000;
@ -297,8 +406,8 @@ public final class popa extends JavaPlugin implements Listener {
first = false; first = false;
playersJson.append(String.format( playersJson.append(String.format(
"{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d}", "{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d,\"afk\":%s}",
playerId, playerName, onlineTime playerId, nameForSite, onlineTime, isAfk ? "true" : "false"
)); ));
} }