From d65dd4ca16e6b1b9769dba5799be34133356893e Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Fri, 6 Apr 2018 09:57:45 +0300 Subject: [PATCH] Added AFK Listener and tracking #538 --- .../plan/data/container/Session.java | 27 +++++++-- .../djrapitops/plan/system/BukkitSystem.java | 1 + .../plan/system/afk/AFKTracker.java | 51 +++++++++++++++++ .../listeners/BukkitListenerSystem.java | 3 +- .../system/listeners/bukkit/AFKListener.java | 55 +++++++++++++++++++ .../bukkit/PlayerOnlineListener.java | 18 ++---- 6 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java create mode 100644 Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java diff --git a/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java b/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java index 22f4d0cd7..f81af4aa0 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/container/Session.java @@ -36,6 +36,8 @@ public class Session { private int mobKills; private int deaths; + private long afkTime; + /** * Creates a new session with given start and end of -1. * @@ -48,15 +50,15 @@ public class Session { playerKills = new ArrayList<>(); mobKills = 0; deaths = 0; + afkTime = 0; } - /** - * Re-Creates a session data object for viewing. - * - * @param sessionStart Epoch millisecond the session was started. - * @param sessionEnd Epoch millisecond the session ended. - */ + @Deprecated public Session(int id, long sessionStart, long sessionEnd, int mobKills, int deaths) { + this(id, sessionStart, sessionEnd, mobKills, deaths, 0); + } + + public Session(int id, long sessionStart, long sessionEnd, int mobKills, int deaths, long afkTime) { this.sessionID = id; this.sessionStart = sessionStart; this.sessionEnd = sessionEnd; @@ -64,6 +66,7 @@ public class Session { this.playerKills = new ArrayList<>(); this.mobKills = mobKills; this.deaths = deaths; + this.afkTime = afkTime; } /** @@ -159,6 +162,18 @@ public class Session { return sessionID != null; } + public void addAFKTime(long timeAFK) { + afkTime += timeAFK; + } + + public long getAfkLength() { + return afkTime; + } + + public long getActiveLength() { + return getLength() - getAfkLength(); + } + /** * Used to get the ID of the session in the Database. * diff --git a/Plan/src/main/java/com/djrapitops/plan/system/BukkitSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/BukkitSystem.java index 81a802a80..63019c76c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/BukkitSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/BukkitSystem.java @@ -30,6 +30,7 @@ import com.djrapitops.plugin.api.utility.log.Log; public class BukkitSystem extends PlanSystem implements ServerSystem { public BukkitSystem(Plan plugin) { + testSystem = this; Log.setErrorManager(new PlanErrorManager()); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java b/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java new file mode 100644 index 000000000..bb306ab00 --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java @@ -0,0 +1,51 @@ +package com.djrapitops.plan.system.afk; + +import com.djrapitops.plan.data.container.Session; +import com.djrapitops.plan.system.cache.SessionCache; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plugin.api.TimeAmount; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * Keeps track how long player has been afk during a session + * + * @author Rsl1122 + */ +public class AFKTracker { + + private final Map lastMovement; + private final long afkThresholdMs; + + public AFKTracker() { + lastMovement = new HashMap<>(); + afkThresholdMs = Settings.AFK_THRESHOLD_MINUTES.getNumber() * TimeAmount.MINUTE.ms(); + } + + public void performedAction(UUID uuid, long time) { + Long lastMoved = lastMovement.getOrDefault(uuid, time); + lastMovement.put(uuid, lastMoved); + + if (time - lastMoved < afkThresholdMs) { + // Threshold not crossed, no action required. + return; + } + + long timeAFK = time - lastMoved; + + Optional cachedSession = SessionCache.getCachedSession(uuid); + if (!cachedSession.isPresent()) { + return; + } + Session session = cachedSession.get(); + session.addAFKTime(timeAFK); + } + + public void loggedOut(UUID uuid, long time) { + performedAction(uuid, time); + lastMovement.remove(uuid); + } +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java index 1d2d4c107..5a569f154 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java @@ -20,7 +20,8 @@ public class BukkitListenerSystem extends ListenerSystem { new GamemodeChangeListener(), new WorldChangeListener(), new CommandPreprocessListener(plugin), - new DeathEventListener() + new DeathEventListener(), + new AFKListener() ); PlayerOnlineListener.setCountKicks(true); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java new file mode 100644 index 000000000..2b7dfe6ae --- /dev/null +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java @@ -0,0 +1,55 @@ +package com.djrapitops.plan.system.listeners.bukkit; + +import com.djrapitops.plan.system.afk.AFKTracker; +import com.djrapitops.plan.utilities.MiscUtils; +import com.djrapitops.plugin.api.utility.log.Log; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerMoveEvent; + +import java.util.UUID; + +/** + * Listener that keeps track of actions that are not considered being AFK. + *

+ * Additional Listener calls in PlayerOnlineListener to avoid having HIGHEST priority listeners. + * + * @author Rsl1122 + * @see PlayerOnlineListener + */ +public class AFKListener implements Listener { + + // Static so that /reload does not cause afk tracking to fail. + public static final AFKTracker AFK_TRACKER = new AFKTracker(); + + private void event(PlayerEvent event) { + try { + UUID uuid = event.getPlayer().getUniqueId(); + long time = MiscUtils.getTime(); + + AFK_TRACKER.performedAction(uuid, time); + } catch (Exception e) { + Log.toLog(this.getClass(), e); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMove(PlayerMoveEvent event) { + event(event); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerChat(AsyncPlayerChatEvent event) { + event(event); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerCommand(PlayerCommandPreprocessEvent event) { + event(event); + } + +} \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java index 0c4037644..31fd2ba4b 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java @@ -69,13 +69,6 @@ public class PlayerOnlineListener implements Listener { } } - /** - * PlayerJoinEvent Listener. - *

- * Adds processing information to the ProcessingQueue. - * - * @param event The Fired event. - */ @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { try { @@ -85,6 +78,8 @@ public class PlayerOnlineListener implements Listener { UUID uuid = player.getUniqueId(); long time = MiscUtils.getTime(); + AFKListener.AFK_TRACKER.performedAction(uuid, time); + String world = player.getWorld().getName(); String gm = player.getGameMode().name(); @@ -110,13 +105,6 @@ public class PlayerOnlineListener implements Listener { } } - /** - * PlayerQuitEvent Listener. - *

- * Adds processing information to the ProcessingQueue. - * - * @param event Fired event - */ @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { try { @@ -124,6 +112,8 @@ public class PlayerOnlineListener implements Listener { Player player = event.getPlayer(); UUID uuid = player.getUniqueId(); + AFKListener.AFK_TRACKER.loggedOut(uuid, time); + Processing.submit(new BanAndOpProcessor(uuid, player.isBanned(), player.isOp())); Processing.submit(new EndSessionProcessor(uuid, time)); Processing.submit(new NetworkPageUpdateProcessor());