diff --git a/pom.xml b/pom.xml index 920fcf6..d837218 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 AuxProtect AuxProtect - 1.3 + 1.3.1 AuxProtect ${project.artifactId}-${project.version} @@ -160,20 +160,20 @@ org.spigotmc spigot-api - 1.20.4-R0.1-SNAPSHOT + 1.21-R0.1-SNAPSHOT provided net.md-5 bungeecord-api - 1.20-R0.1-SNAPSHOT + 1.21-R0.1-SNAPSHOT jar provided net.md-5 bungeecord-api - 1.20-R0.1-SNAPSHOT + 1.21-R0.1-SNAPSHOT javadoc provided diff --git a/src/main/java/dev/heliosares/auxprotect/core/Activity.java b/src/main/java/dev/heliosares/auxprotect/core/Activity.java new file mode 100644 index 0000000..004080a --- /dev/null +++ b/src/main/java/dev/heliosares/auxprotect/core/Activity.java @@ -0,0 +1,34 @@ +package dev.heliosares.auxprotect.core; + +public enum Activity { + + IN_SPAWN('$', 100), + COMMAND('/', 5), + CHAT('c', 5), + DAMAGE('d', 0.25), + INTERACT_ENTITY('e', 1), + INTERACT_BLOCK('f', 1), + INTERACT_AIR('g', 1), + CLICK_ITEM('i', 1), + OPEN_INVENTORY('o', 1), + PICKUP('p', 1), + DROP('q', 1), + BLOCK_BREAK('r', 1), + BLOCK_PLACE('s', 1); + + + public final char character; + public final double score; + + Activity(char character, double score) { + this.character = character; + this.score = score; + } + + public static Activity getByChar(char c) { + for (Activity value : values()) { + if (value.character == c) return value; + } + return null; + } +} diff --git a/src/main/java/dev/heliosares/auxprotect/core/ActivityRecord.java b/src/main/java/dev/heliosares/auxprotect/core/ActivityRecord.java new file mode 100644 index 0000000..8502e00 --- /dev/null +++ b/src/main/java/dev/heliosares/auxprotect/core/ActivityRecord.java @@ -0,0 +1,85 @@ +package dev.heliosares.auxprotect.core; + +import net.md_5.bungee.api.ChatColor; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public record ActivityRecord(@Nonnull List activities, double distanceMoved) { + @Override + public String toString() { + StringBuilder activityString = new StringBuilder(getActivityString()); + + activityString.append(";"); + final double moved = distanceMoved(); + if (moved > 1E-6) { + if (moved >= 10) { + activityString.append((int) Math.round(moved)); + } else { + activityString.append(Math.round(moved * 10) / 10D); + } + } + return activityString.toString(); + } + + public String getActivityString() { + StringBuilder activityString = new StringBuilder(); + + for (Activity a : activities()) { + activityString.append(a.character); + } + + return activityString.toString(); + } + + public double countScore() { + double score = Math.floor((distanceMoved()) / 10); + + if (distanceMoved() > 1E-6) score++; + + for (Activity activity : activities()) { + score += activity.score; + } + + return score; + } + + public static ActivityRecord parse(String data) throws IllegalArgumentException { + if (data == null) return null; + + if (data.matches("\\d+")) { + throw new IllegalArgumentException("Legacy activity"); + } + if (!data.matches("[^;]*;(\\d+(\\.\\d)?)?")) { + throw new IllegalArgumentException("Invalid activity string"); + } + List activities = new ArrayList<>(); + if (data.startsWith(";")) data = " " + data; + if (data.endsWith(";")) data += " "; + String[] parts = data.split(";"); + + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid activity string format"); + } + + for (char c : parts[0].trim().toCharArray()) { + activities.add(Activity.getByChar(c)); + } + + double distance = parts[1].isBlank() ? 0 : Double.parseDouble(parts[1].trim()); + + return new ActivityRecord(activities, distance); + } + + public String getHoverText() { + StringBuilder hoverText = new StringBuilder("\n\n"); + hoverText.append(ChatColor.COLOR_CHAR + "7Activity: " + ChatColor.COLOR_CHAR + "9").append(getActivityString()).append("\n"); + for (Activity activity : new HashSet<>(activities())) { + hoverText.append(ChatColor.COLOR_CHAR + "7 ").append(activity.character).append(" = ").append(activity.toString().toLowerCase()).append(" (").append(activity.score).append(")\n"); + } + hoverText.append(ChatColor.COLOR_CHAR + "7Moved " + ChatColor.COLOR_CHAR + "9").append(distanceMoved()).append(ChatColor.COLOR_CHAR).append("7 Blocks"); + return hoverText.toString(); + } +} diff --git a/src/main/java/dev/heliosares/auxprotect/database/Results.java b/src/main/java/dev/heliosares/auxprotect/database/Results.java index 3fde922..39b153f 100644 --- a/src/main/java/dev/heliosares/auxprotect/database/Results.java +++ b/src/main/java/dev/heliosares/auxprotect/database/Results.java @@ -1,9 +1,9 @@ package dev.heliosares.auxprotect.database; import dev.heliosares.auxprotect.adapters.sender.SenderAdapter; -import dev.heliosares.auxprotect.api.AuxProtectAPI; import dev.heliosares.auxprotect.core.APPermission; import dev.heliosares.auxprotect.core.APPlayer; +import dev.heliosares.auxprotect.core.ActivityRecord; import dev.heliosares.auxprotect.core.IAuxProtect; import dev.heliosares.auxprotect.core.Language; import dev.heliosares.auxprotect.core.Parameters; @@ -24,6 +24,9 @@ import org.bukkit.inventory.ItemStack; import java.io.IOException; import java.sql.SQLException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.List; import java.util.Objects; import java.util.Random; @@ -185,14 +188,32 @@ public class Results { } String data = entry.getData(); if (data != null && !data.isEmpty()) { + HoverEvent hoverEvent = clickToCopy; + if (entry.getAction().equals(EntryAction.ACTIVITY)) { + try { + ActivityRecord record = ActivityRecord.parse(data); + if (record != null) { + message.append(" " + org.bukkit.ChatColor.COLOR_CHAR + "a" + record.countScore()); + hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.L.RESULTS__CLICK_TO_COPY.translate() + record.getHoverText())); + } + } catch (IllegalArgumentException ignored) { + } + } if (entry.getAction().equals(EntryAction.SESSION) && !APPermission.LOOKUP_ACTION.dot(EntryAction.SESSION.toString().toLowerCase()).dot("ip").hasPermission(player)) { message.append(" " + ChatColor.COLOR_CHAR + "8[" + ChatColor.COLOR_CHAR + "7" + Language.L.RESULTS__REDACTED.translate() + ChatColor.COLOR_CHAR + "8]"); message.event((ClickEvent) null).event((HoverEvent) null); } else { message.append(" " + ChatColor.COLOR_CHAR + "8[" + ChatColor.COLOR_CHAR + "7" + data + ChatColor.COLOR_CHAR + "8]"); - message.event(clickToCopy).event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, data)); + message.event(hoverEvent).event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, data)); } } + if (entry.getAction().equals(EntryAction.ACTIVITY)) { + message.append(" " + ChatColor.COLOR_CHAR + "8[" + ChatColor.COLOR_CHAR + "7Copy Minute Range" + ChatColor.COLOR_CHAR + "8]"); + ZonedDateTime zonedDateTime = Instant.ofEpochMilli(entry.getTime()).atZone(ZoneId.systemDefault()); + ZonedDateTime start = zonedDateTime.withSecond(0).withNano(0); + ZonedDateTime end = start.plusMinutes(1).minusNanos(1000000); + message.event(clickToCopy).event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, start.toInstant().toEpochMilli() + "e-" + end.toInstant().toEpochMilli() + "e")); + } } } if (entry.getWorld() != null && !entry.getWorld().equals("$null") && coords) { diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/APPlayerSpigot.java b/src/main/java/dev/heliosares/auxprotect/spigot/APPlayerSpigot.java index 33ca003..f9447fc 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/APPlayerSpigot.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/APPlayerSpigot.java @@ -1,6 +1,8 @@ package dev.heliosares.auxprotect.spigot; import dev.heliosares.auxprotect.core.APPlayer; +import dev.heliosares.auxprotect.core.Activity; +import dev.heliosares.auxprotect.core.ActivityRecord; import dev.heliosares.auxprotect.core.IAuxProtect; import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.EntryAction; @@ -13,11 +15,12 @@ import org.bukkit.inventory.ItemStack; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Objects; public class APPlayerSpigot extends APPlayer { - public final double[] activity = new double[30]; + private final List activityStack = new ArrayList<>(); + private ArrayList currentActivity; private final Player player; private final List posBlob = new ArrayList<>(); public long lastLoggedMoney; @@ -25,12 +28,10 @@ public class APPlayerSpigot extends APPlayer { public long lastLoggedInventoryDiff; public long lastLoggedPos; public long lastMoved; - public long lastLoggedActivity; public Location lastLocation; public long lastCheckedMovement; - public double movedAmountThisMinute; + private double movedAmountThisMinute; public boolean hasMovedThisMinute; - public int activityIndex; public long lastNotifyInactive; // hotbar, main, armor, offhand, echest private List invDiffItems; @@ -41,7 +42,6 @@ public class APPlayerSpigot extends APPlayer { super(plugin, player); this.player = player; - Arrays.fill(activity, -1); } @Override @@ -49,8 +49,45 @@ public class APPlayerSpigot extends APPlayer { return player.getName(); } - public void addActivity(double d) { - activity[activityIndex] += d; + public void addActivity(Activity a) { + synchronized (activityStack) { + if (currentActivity == null) currentActivity = new ArrayList<>(); + currentActivity.add(a); + } + } + + public String concludeActivityForMinute() { + synchronized (activityStack) { + while (activityStack.size() >= 30) { + activityStack.remove(0); + } + ActivityRecord record = null; + if (currentActivity != null || movedAmountThisMinute > 1E-6) { + if (currentActivity == null) currentActivity = new ArrayList<>(); + record = new ActivityRecord(currentActivity, movedAmountThisMinute); + } + activityStack.add(record); + currentActivity = null; + movedAmountThisMinute = 0; + hasMovedThisMinute = false; + + if (record == null) return null; + return record.toString(); + } + } + + public void move() { + synchronized (activityStack) { + if (lastLocation != null && Objects.equals(lastLocation.getWorld(), getPlayer().getWorld())) { + movedAmountThisMinute += Math.min(lastLocation.distance(getPlayer().getLocation()), 10); + } + lastLocation = getPlayer().getLocation(); + lastCheckedMovement = System.currentTimeMillis(); + } + } + + public List getActivityStack() { + return activityStack; } public long logInventory(String reason) { diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/AuxProtectSpigot.java b/src/main/java/dev/heliosares/auxprotect/spigot/AuxProtectSpigot.java index 4c57c8c..0a972a2 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/AuxProtectSpigot.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/AuxProtectSpigot.java @@ -6,6 +6,8 @@ import dev.heliosares.auxprotect.adapters.sender.SpigotSenderAdapter; import dev.heliosares.auxprotect.api.AuxProtectAPI; import dev.heliosares.auxprotect.core.APConfig; import dev.heliosares.auxprotect.core.APPermission; +import dev.heliosares.auxprotect.core.Activity; +import dev.heliosares.auxprotect.core.ActivityRecord; import dev.heliosares.auxprotect.core.IAuxProtect; import dev.heliosares.auxprotect.core.Language; import dev.heliosares.auxprotect.core.PlatformType; @@ -64,6 +66,7 @@ import java.sql.SQLException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -358,6 +361,7 @@ public final class AuxProtectSpigot extends JavaPlugin implements IAuxProtect { new BukkitRunnable() { private boolean running; + private int lastLoggedActivityMinute = -1; @Override public void run() { @@ -371,6 +375,15 @@ public final class AuxProtectSpigot extends JavaPlugin implements IAuxProtect { synchronized (apPlayers) { players = new ArrayList<>(apPlayers.values()); } + Calendar calendar = Calendar.getInstance(); + int minute = calendar.get(Calendar.MINUTE); + int second = calendar.get(Calendar.SECOND); + + boolean logActivity = lastLoggedActivityMinute != minute && second >= 30; + // Put in the middle of the minute to make parsing it later easier + if (logActivity) { + lastLoggedActivityMinute = minute; + } for (APPlayerSpigot apPlayer : players) { if (!apPlayer.getPlayer().isOnline()) { continue; @@ -403,43 +416,25 @@ public final class AuxProtectSpigot extends JavaPlugin implements IAuxProtect { } if (System.currentTimeMillis() - apPlayer.lastCheckedMovement >= 1000) { - if (apPlayer.lastLocation != null - && Objects.equals(apPlayer.lastLocation.getWorld(), apPlayer.getPlayer().getWorld())) { - apPlayer.movedAmountThisMinute += Math - .min(apPlayer.lastLocation.distance(apPlayer.getPlayer().getLocation()), 10); - } - apPlayer.lastLocation = apPlayer.getPlayer().getLocation(); apPlayer.lastCheckedMovement = System.currentTimeMillis(); + apPlayer.move(); } - if (apPlayer.lastLoggedActivity == 0) { - apPlayer.lastLoggedActivity = System.currentTimeMillis(); - } - if (System.currentTimeMillis() - apPlayer.lastLoggedActivity > 60000L && config.isPrivate()) { + if (logActivity && config.isPrivate()) { if (apPlayer.getPlayer().getWorld().getName().equals("flat") && config.isPrivate()) { - apPlayer.activity[apPlayer.activityIndex] += 100; - } - apPlayer.addActivity(Math.floor((apPlayer.movedAmountThisMinute + 7) / 10)); - apPlayer.movedAmountThisMinute = 0; - - if (apPlayer.hasMovedThisMinute) { - apPlayer.addActivity(1); - apPlayer.hasMovedThisMinute = false; + apPlayer.addActivity(Activity.IN_SPAWN); } - add(new DbEntry(AuxProtectSpigot.getLabel(apPlayer.getPlayer()), EntryAction.ACTIVITY, false, - apPlayer.getPlayer().getLocation(), "", - (int) Math.round(apPlayer.activity[apPlayer.activityIndex]) + "")); - apPlayer.lastLoggedActivity = System.currentTimeMillis(); + add(new DbEntry(AuxProtectSpigot.getLabel(apPlayer.getPlayer()), EntryAction.ACTIVITY, false, apPlayer.getPlayer().getLocation(), "", apPlayer.concludeActivityForMinute())); int tallied = 0; int inactive = 0; - for (double activity : apPlayer.activity) { - if (activity < 0) { + for (ActivityRecord record : apPlayer.getActivityStack()) { + if (record == null || record.activities().isEmpty()) { continue; } tallied++; - if (activity < 10) { + if (record.countScore() < 10) { inactive++; } } @@ -459,12 +454,6 @@ public final class AuxProtectSpigot extends JavaPlugin implements IAuxProtect { apPlayer.getPlayer().getLocation(), "inactive", inactive + "/" + tallied)); } } - - apPlayer.activityIndex++; - if (apPlayer.activityIndex >= apPlayer.activity.length) { - apPlayer.activityIndex = 0; - } - apPlayer.activity[apPlayer.activityIndex] = 0; } } } finally { diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/EntityListener.java b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/EntityListener.java index ecbb01f..663ee97 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/EntityListener.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/EntityListener.java @@ -1,6 +1,7 @@ package dev.heliosares.auxprotect.spigot.listeners; import dev.heliosares.auxprotect.core.APPermission; +import dev.heliosares.auxprotect.core.Activity; import dev.heliosares.auxprotect.core.IAuxProtect; import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.EntryAction; @@ -26,8 +27,10 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.EntityDropItemEvent; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityMountEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityResurrectEvent; import org.bukkit.event.entity.EntityTameEvent; @@ -41,8 +44,6 @@ import org.bukkit.inventory.meta.MapMeta; import org.bukkit.map.MapRenderer; import org.bukkit.projectiles.ProjectileSource; import org.bukkit.scheduler.BukkitRunnable; -import org.spigotmc.event.entity.EntityDismountEvent; -import org.spigotmc.event.entity.EntityMountEvent; import java.util.ArrayList; @@ -147,7 +148,7 @@ public class EntityListener implements Listener { if (e.getCause() == DamageCause.THORNS) { itemname += "THORNS"; } else if (source instanceof Player sourcePl) { - plugin.getAPPlayer(sourcePl).addActivity(0.25); + plugin.getAPPlayer(sourcePl).addActivity(Activity.DAMAGE); itemname += sourcePl.getInventory().getItemInMainHand().getType().toString().toLowerCase(); } @@ -252,7 +253,7 @@ public class EntityListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerDropEvent(PlayerDropItemEvent e) { - plugin.getAPPlayer(e.getPlayer()).addActivity(1); + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.DROP); if (isChartMap(e.getItemDrop().getItemStack())) { e.getItemDrop().remove(); @@ -266,7 +267,7 @@ public class EntityListener implements Listener { public void onPickupEvent(EntityPickupItemEvent e) { if (e.getEntity() instanceof Player player) { - plugin.getAPPlayer(player).addActivity(1); + plugin.getAPPlayer(player).addActivity(Activity.PICKUP); if (isChartMap(e.getItem().getItemStack()) && !APPermission.LOOKUP_MONEY.hasPermission(player)) { e.setCancelled(true); diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/InventoryListener.java b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/InventoryListener.java index 1d45f32..ee4a8b4 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/InventoryListener.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/InventoryListener.java @@ -1,6 +1,7 @@ package dev.heliosares.auxprotect.spigot.listeners; import dev.heliosares.auxprotect.api.AuxProtectAPI; +import dev.heliosares.auxprotect.core.Activity; import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.EntryAction; import dev.heliosares.auxprotect.database.SingleItemEntry; @@ -10,6 +11,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -35,6 +37,7 @@ public class InventoryListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryOpenEvent(InventoryOpenEvent e) { + plugin.getAPPlayer((Player) e.getPlayer()).addActivity(Activity.OPEN_INVENTORY); log(e.getPlayer(), e.getInventory(), true); } @@ -79,11 +82,12 @@ public class InventoryListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryClick(InventoryClickEvent e) { + plugin.getAPPlayer((Player) e.getWhoClicked()).addActivity(Activity.CLICK_ITEM); + InventoryType type = e.getWhoClicked().getOpenInventory().getTopInventory().getType(); if (e.getSlotType() != InventoryType.SlotType.RESULT) return; if (e.getCurrentItem() == null || e.getCurrentItem().getType() == Material.AIR) return; - EntryAction action; String data = ""; ItemStack[] entryItems; diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java index 6cb2507..86f583a 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java @@ -2,6 +2,7 @@ package dev.heliosares.auxprotect.spigot.listeners; import dev.heliosares.auxprotect.adapters.sender.SpigotSenderAdapter; import dev.heliosares.auxprotect.core.APPermission; +import dev.heliosares.auxprotect.core.Activity; import dev.heliosares.auxprotect.core.Language; import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.EntryAction; @@ -27,6 +28,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityPlaceEvent; import org.bukkit.event.entity.EntityToggleGlideEvent; import org.bukkit.event.entity.PlayerLeashEntityEvent; @@ -115,7 +118,7 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent e) { - plugin.getAPPlayer(e.getPlayer()).addActivity(1); + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.INTERACT_ENTITY); ItemStack item = e.getPlayer().getInventory().getItem(e.getHand()); @@ -156,7 +159,7 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerInteractEvent(PlayerInteractEvent e) { - plugin.getAPPlayer(e.getPlayer()).addActivity(1); + plugin.getAPPlayer(e.getPlayer()).addActivity(e.getClickedBlock() == null ? Activity.INTERACT_AIR : Activity.INTERACT_BLOCK); if (e.useInteractedBlock() == Result.DENY) { return; @@ -351,7 +354,7 @@ public class PlayerListener implements Listener { return; } - boolean tether = entity.getLeashHolder().getType() == EntityType.LEASH_HITCH; + boolean tether = entity.getLeashHolder().getType().toString().startsWith("LEASH_"); DbEntry entry = new DbEntry(AuxProtectSpigot.getLabel(e.getPlayer()), EntryAction.LEASH, false, e.getEntity().getLocation(), AuxProtectSpigot.getLabel(e.getEntity()), tether ? "was tethered" : ""); @@ -366,7 +369,7 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onCommand(PlayerCommandPreprocessEvent e) { - plugin.getAPPlayer(e.getPlayer()).addActivity(5); + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.COMMAND); plugin.add(new DbEntry(AuxProtectSpigot.getLabel(e.getPlayer()), EntryAction.COMMAND, false, e.getPlayer().getLocation(), e.getMessage(), "")); @@ -374,11 +377,21 @@ public class PlayerListener implements Listener { @EventHandler(ignoreCancelled = true) public void onChat(AsyncPlayerChatEvent e) { - plugin.getAPPlayer(e.getPlayer()).addActivity(5); + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.CHAT); plugin.add(new DbEntry(AuxProtectSpigot.getLabel(e.getPlayer()), EntryAction.CHAT, false, e.getPlayer().getLocation(), e.getMessage().trim(), "")); if (plugin.getAPConfig().isDemoMode()) { e.getPlayer().sendMessage(ChatColor.COLOR_CHAR + "cChat is disabled."); e.setCancelled(true); } } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onBlockPlace(BlockPlaceEvent e) { + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.BLOCK_PLACE); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onBlockBreak(BlockBreakEvent e) { + plugin.getAPPlayer(e.getPlayer()).addActivity(Activity.BLOCK_BREAK); + } } diff --git a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/ProjectileListener.java b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/ProjectileListener.java index 05db829..8ff5253 100644 --- a/src/main/java/dev/heliosares/auxprotect/spigot/listeners/ProjectileListener.java +++ b/src/main/java/dev/heliosares/auxprotect/spigot/listeners/ProjectileListener.java @@ -29,11 +29,14 @@ public class ProjectileListener implements Listener { this.whitelist = new ArrayList<>(); whitelist.add(EntityType.ENDER_PEARL); whitelist.add(EntityType.TRIDENT); - whitelist.add(EntityType.FISHING_HOOK); whitelist.add(EntityType.SNOWBALL); whitelist.add(EntityType.EGG); - whitelist.add(EntityType.SPLASH_POTION); whitelist.add(EntityType.ARROW); + for (EntityType type : EntityType.values()) { + if (type.toString().contains("POTION") || type.toString().contains("FISHING_")) { + whitelist.add(type); + } + } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) diff --git a/src/main/java/dev/heliosares/auxprotect/utils/ActivitySolver.java b/src/main/java/dev/heliosares/auxprotect/utils/ActivitySolver.java index 183fea0..0945b6e 100644 --- a/src/main/java/dev/heliosares/auxprotect/utils/ActivitySolver.java +++ b/src/main/java/dev/heliosares/auxprotect/utils/ActivitySolver.java @@ -1,5 +1,6 @@ package dev.heliosares.auxprotect.utils; +import dev.heliosares.auxprotect.core.ActivityRecord; import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.EntryAction; import dev.heliosares.auxprotect.spigot.AuxProtectSpigot; @@ -23,6 +24,8 @@ import java.util.List; public class ActivitySolver { public static BaseComponent[][] solveActivity(List entries, long rangeStart, long rangeEnd) { + if (entries.isEmpty()) return null; + ComponentBuilder message = new ComponentBuilder().append("", FormatRetention.NONE); LocalDateTime startTime = Instant.ofEpochMilli(rangeStart).atZone(ZoneId.systemDefault()).toLocalDateTime() .withSecond(0).withNano(0); @@ -30,7 +33,7 @@ public class ActivitySolver { DateTimeFormatter formatterHour = DateTimeFormatter.ofPattern("Ka"); final long startMillis = startTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); final int minutes = (int) Math.ceil((rangeEnd - rangeStart) / 1000.0 / 60.0); - int[] counter = new int[minutes]; + double[] counter = new double[minutes]; Location[] locations = new Location[minutes]; Arrays.fill(counter, -1); StringBuilder line = new StringBuilder("" + ChatColor.COLOR_CHAR + "7" + ChatColor.COLOR_CHAR + "m"); @@ -41,6 +44,9 @@ public class ActivitySolver { components.add(message.create()); message = new ComponentBuilder(); + ActivityRecord[] activityRecords = new ActivityRecord[minutes]; + long[] times = new long[minutes]; + long lastTime = startMillis; for (int i = entries.size() - 1, minute = 0; i >= 0; i--) { DbEntry entry = entries.get(i); @@ -70,9 +76,23 @@ public class ActivitySolver { counter[minute] = 0; } - int activity = Integer.parseInt(entry.getData()); + + double activity = 0; + try { + ActivityRecord record = ActivityRecord.parse(entry.getData()); + activityRecords[minute] = record; + if (record != null) { + activity = record.countScore(); + } + } catch (IllegalArgumentException e) { + try { + activity = Integer.parseInt(entry.getData()); + } catch (IllegalArgumentException ignored) { + } + } counter[minute] += activity; locations[minute] = new Location(Bukkit.getWorld(entry.getWorld()), entry.getX(), entry.getY(), entry.getZ()); + times[minute] = entry.getTime(); lastTime = entry.getTime(); minute++; @@ -103,7 +123,7 @@ public class ActivitySolver { message.color(ChatColor.BLACK); } else { - int activity = counter[i]; + double activity = counter[i]; String hovertext = ChatColor.COLOR_CHAR + "9" + time.format(formatterDateTime) + "\n"; @@ -116,14 +136,19 @@ public class ActivitySolver { } ClickEvent clickevent = null; if (locations[i] != null) { - hovertext += String.format("\n\n" + ChatColor.COLOR_CHAR + "7(x%d/y%d/z%d/%s)\n" + ChatColor.COLOR_CHAR + "7Click to teleport", locations[i].getBlockX(), + hovertext += String.format("\n\n" + ChatColor.COLOR_CHAR + "7(x%d/y%d/z%d/%s)\n", locations[i].getBlockX(), locations[i].getBlockY(), locations[i].getBlockZ(), locations[i].getWorld().getName()); - clickevent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, - String.format("/auxprotect tp %d %d %d %s", locations[i].getBlockX(), - locations[i].getBlockY(), locations[i].getBlockZ(), - locations[i].getWorld().getName())); } + ActivityRecord activityRecord = activityRecords[i]; + if (activityRecord != null) { + hovertext += activityRecord.getHoverText(); + } + + hovertext += "\n\n"; + hovertext += ChatColor.COLOR_CHAR + "9Click to view"; + clickevent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/auxprotect lookup a:activity t:" + times[i] + "e"); + message.append(AuxProtectSpigot.BLOCK + "") .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(hovertext))).event(clickevent); if (activity >= 20) { diff --git a/src/main/test/dev/heliosares/auxprotect/core/ActivityTest.java b/src/main/test/dev/heliosares/auxprotect/core/ActivityTest.java new file mode 100644 index 0000000..34b1879 --- /dev/null +++ b/src/main/test/dev/heliosares/auxprotect/core/ActivityTest.java @@ -0,0 +1,56 @@ +package dev.heliosares.auxprotect.core; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ActivityTest { + @Test + public void checkNoDuplicateCharacters() { + Set chars = new HashSet<>(); + for (Activity activity : Activity.values()) { + if (!chars.add(activity.character)) { + throw new IllegalArgumentException("Duplicate characters " + activity.character); + } + } + } + + @Test + public void testActivityParsing() { + ActivityRecord record = new ActivityRecord(List.of(Activity.values()), 0.69); + String data = record.toString(); + System.out.println(data); + + ActivityRecord parsed = ActivityRecord.parse(data); + String dataParsed = parsed.toString(); + + assertEquals(data, dataParsed); + + assertEquals(record.distanceMoved(), parsed.distanceMoved(), 0.1); + + assertEquals(record.activities(), parsed.activities()); + + assertNotNull(ActivityRecord.parse(";")); + assertNotNull(ActivityRecord.parse(";0")); + assertNotNull(ActivityRecord.parse(";0.0")); + assertNotNull(ActivityRecord.parse("/;0.0")); + assertNotNull(ActivityRecord.parse("/;0")); + assertNotNull(ActivityRecord.parse("/;")); + assertNotNull(ActivityRecord.parse("///;")); + } + + @Test + public void testActivityScoring() { + ActivityRecord record = new ActivityRecord(List.of(Activity.values()), 0); + double totalScore = 0; + for (Activity activity : Activity.values()) { + totalScore += activity.score; + } + assertEquals(totalScore, record.countScore(), 1E-6); + } +}