diff --git a/src/dev/heliosares/auxprotect/core/commands/DumpCommand.java b/src/dev/heliosares/auxprotect/core/commands/DumpCommand.java index e59e005..7b7d493 100644 --- a/src/dev/heliosares/auxprotect/core/commands/DumpCommand.java +++ b/src/dev/heliosares/auxprotect/core/commands/DumpCommand.java @@ -6,9 +6,9 @@ import dev.heliosares.auxprotect.core.Command; import dev.heliosares.auxprotect.core.IAuxProtect; import dev.heliosares.auxprotect.core.Language; import dev.heliosares.auxprotect.database.ConnectionPool; -import dev.heliosares.auxprotect.database.Results; import dev.heliosares.auxprotect.database.Table; import dev.heliosares.auxprotect.utils.StackUtil; +import dev.heliosares.auxprotect.utils.TimeUtil; import java.io.BufferedWriter; import java.io.File; @@ -31,9 +31,9 @@ public class DumpCommand extends Command { boolean stats) throws Exception { StringBuilder trace = new StringBuilder(); if (!stats) { - trace.append("Generated: ").append(LocalDateTime.now().format(Results.dateFormatter)).append(" (").append(System.currentTimeMillis()).append(")\n"); + trace.append("Generated: ").append(LocalDateTime.now().format(TimeUtil.entryTimeFormat)).append(" (").append(System.currentTimeMillis()).append(")\n"); trace.append("Connected: ").append(LocalDateTime.ofInstant(Instant.ofEpochMilli(plugin.getSqlManager().getTimeConnected()), ZoneId.systemDefault()) - .format(Results.dateFormatter)).append(" (").append(plugin.getSqlManager().getTimeConnected()).append(")\n"); + .format(TimeUtil.entryTimeFormat)).append(" (").append(plugin.getSqlManager().getTimeConnected()).append(")\n"); } trace.append("Plugin version: ").append(plugin.getPluginVersion()).append("\n"); trace.append("Key: "); diff --git a/src/dev/heliosares/auxprotect/database/PosEntry.java b/src/dev/heliosares/auxprotect/database/PosEntry.java index 5aa3b24..329e1bd 100644 --- a/src/dev/heliosares/auxprotect/database/PosEntry.java +++ b/src/dev/heliosares/auxprotect/database/PosEntry.java @@ -22,9 +22,9 @@ public class PosEntry extends DbEntry { protected PosEntry(long time, int uid, EntryAction action, boolean state, String world, int x, int y, int z, byte increment, int pitch, int yaw, String target, int target_id, String data) { super(time, uid, action, state, world, x, y, z, pitch, yaw, target, target_id, data); double[] dInc = PosEncoder.byteToFractions(increment); - this.x = x + dInc[0] * (x < 0 ? -1 : 1); - this.y = y + dInc[1] * (y < 0 ? -1 : 1); - this.z = z + dInc[2] * (z < 0 ? -1 : 1); + this.x = x + dInc[0]; + this.y = y + dInc[1]; + this.z = z + dInc[2]; } public PosEntry(long time, int uid, Location location) { @@ -50,17 +50,17 @@ public class PosEntry extends DbEntry { @Override public int getX() { - return (int) x; + return (int) Math.floor(x); } @Override public int getY() { - return (int) y; + return (int) Math.floor(y); } @Override public int getZ() { - return (int) z; + return (int) Math.floor(z); } public byte getIncrement() { diff --git a/src/dev/heliosares/auxprotect/database/Results.java b/src/dev/heliosares/auxprotect/database/Results.java index 130ad30..659fc5c 100644 --- a/src/dev/heliosares/auxprotect/database/Results.java +++ b/src/dev/heliosares/auxprotect/database/Results.java @@ -16,15 +16,11 @@ import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.hover.content.Text; import java.sql.SQLException; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Random; public class Results { - public static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("ddMMMyy HH:mm:ss.SSS"); protected final SenderAdapter player; final IAuxProtect plugin; private final List entries; @@ -73,7 +69,7 @@ public class Results { msg = String.format("§7%s ago", TimeUtil.millisToString(System.currentTimeMillis() - entry.getTime())); } message.append(msg).event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - new Text(Instant.ofEpochMilli(entry.getTime()).atZone(ZoneId.systemDefault()).format(dateFormatter) + new Text(TimeUtil.format(entry.getTime(), TimeUtil.entryTimeFormat) + "\n§7Click to copy epoch time. (" + entry.getTime() + "ms)"))) .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, entry.getTime() + "e")); } diff --git a/src/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java b/src/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java index f2be9e0..8d678fe 100644 --- a/src/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java +++ b/src/dev/heliosares/auxprotect/spigot/listeners/PlayerListener.java @@ -7,6 +7,7 @@ import dev.heliosares.auxprotect.database.EntryAction; import dev.heliosares.auxprotect.database.SingleItemEntry; import dev.heliosares.auxprotect.spigot.AuxProtectSpigot; import dev.heliosares.auxprotect.utils.InvSerialization; +import dev.heliosares.auxprotect.utils.PlaybackSolver; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; @@ -33,6 +34,7 @@ import org.bukkit.scheduler.BukkitRunnable; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Objects; public class PlayerListener implements Listener { @@ -219,13 +221,16 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerTeleport(PlayerTeleportEvent e) { + if (e.getTo() == null) return; APPlayer apPlayer = plugin.getAPPlayer(e.getPlayer()); apPlayer.logPreTeleportPos(e.getFrom()); apPlayer.logPostTeleportPos(e.getTo()); apPlayer.lastLoggedPos = System.currentTimeMillis(); - if (!plugin.getAPConfig().isInventoryOnWorldChange() || e.getFrom().getWorld().equals(e.getTo().getWorld())) { - return; - } + boolean sameWorld = Objects.equals(e.getFrom().getWorld(), e.getTo().getWorld()); + if (!sameWorld || e.getFrom().distance(e.getTo()) > 64) PlaybackSolver.close(e.getPlayer().getUniqueId()); + + if (!plugin.getAPConfig().isInventoryOnWorldChange() || sameWorld) return; + byte[] inventory_ = null; try { inventory_ = InvSerialization.playerToByteArray(e.getPlayer()); diff --git a/src/dev/heliosares/auxprotect/utils/PlaybackSolver.java b/src/dev/heliosares/auxprotect/utils/PlaybackSolver.java index e417b23..57c6c19 100644 --- a/src/dev/heliosares/auxprotect/utils/PlaybackSolver.java +++ b/src/dev/heliosares/auxprotect/utils/PlaybackSolver.java @@ -10,6 +10,8 @@ import dev.heliosares.auxprotect.database.DbEntry; import dev.heliosares.auxprotect.database.DbEntryBukkit; import dev.heliosares.auxprotect.exceptions.LookupException; import dev.heliosares.auxprotect.spigot.AuxProtectSpigot; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -27,20 +29,20 @@ public class PlaybackSolver extends BukkitRunnable { private final long realReferenceTime; private final Map actors = new HashMap<>(); - private boolean closed; - private final ProtocolManager protocol; private final Player audience; private final Map skins = new HashMap<>(); + private boolean closed; public PlaybackSolver(IAuxProtect plugin, SenderAdapter sender, List entries, long startTime) throws SQLException, LookupException { if (plugin.getPlatform() != PlatformType.SPIGOT) throw new UnsupportedOperationException(); - PlaybackSolver instance = instances.get(sender.getUniqueId()); - if (instance != null) { - instance.close(); - } + + close(sender.getUniqueId()); + this.audience = (Player) sender.getSender(); - instances.put(sender.getUniqueId(), this); + synchronized (instances) { + instances.put(sender.getUniqueId(), this); + } realReferenceTime = System.currentTimeMillis(); points = getLocations(plugin, entries, startTime).stream() // Ensures the entries are in the same world @@ -101,7 +103,6 @@ public class PlaybackSolver extends BukkitRunnable { if (inc.hasYaw()) incLoc.setYaw(inc.yaw()); lastLoc = incLoc.clone(); PosPoint point = new PosPoint(time, UUID.fromString(entry.getUserUUID().substring(1)), entry.getUser(), entry.getUid(), incLoc.clone(), true); - plugin.debug("Adding point " + point, 3); points.add(point); } } @@ -109,7 +110,6 @@ public class PlaybackSolver extends BukkitRunnable { entryLoc.setYaw(entry.getYaw()); entryLoc.setPitch(entry.getPitch()); PosPoint point = new PosPoint(entry.getTime(), UUID.fromString(entry.getUserUUID().substring(1)), entry.getUser(), entry.getUid(), entryLoc, false); - plugin.debug("Adding point " + point, 3); points.add(point); lastEntries.put(entry.getUser(), entry); } @@ -117,17 +117,36 @@ public class PlaybackSolver extends BukkitRunnable { return points; } + public static void close(UUID uuid) { + synchronized (instances) { + PlaybackSolver instance = instances.get(uuid); + if (instance != null) instance.close(); + } + } + + public static void cleanup() { + synchronized (instances) { + instances.values().removeIf(PlaybackSolver::isClosed); + } + } + @Override public void run() { - if (closed || isCancelled()) { - audience.sendMessage(Language.translate(Language.L.COMMAND__LOOKUP__PLAYBACK__STOPPED)); - actors.values().forEach(FakePlayer::remove); + if (closed || isCancelled() || !audience.isOnline()) { + close(); + if (audience.isOnline()) { + audience.sendMessage(Language.translate(Language.L.COMMAND__LOOKUP__PLAYBACK__STOPPED)); + audience.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(Language.translate(Language.L.COMMAND__LOOKUP__PLAYBACK__STOPPED))); + actors.values().forEach(FakePlayer::remove); + } actors.clear(); cancel(); return; } final long timeNow = System.currentTimeMillis() - realReferenceTime + startTime; + audience.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(TimeUtil.format(timeNow, TimeUtil.entryTimeFormat) + " §7- " + TimeUtil.millisToString(System.currentTimeMillis() - timeNow) + " ago")); + for (Iterator it = points.iterator(); it.hasNext(); ) { PosPoint point = it.next(); @@ -137,7 +156,9 @@ public class PlaybackSolver extends BukkitRunnable { assert loc.getWorld() != null; if (actor == null) { - actor = new FakePlayer(point.name(), protocol, audience); + String name = "~" + point.name; + if (name.length() > 16) name = name.substring(0, 16); + actor = new FakePlayer(name, protocol, audience); actor.spawn(point.location(), skins.get(point.uuid)); } actors.put(point.name(), actor); @@ -157,10 +178,14 @@ public class PlaybackSolver extends BukkitRunnable { if (points.isEmpty()) close(); } - public void close() { if (closed) return; closed = true; + cleanup(); + } + + public boolean isClosed() { + return closed; } public record PosPoint(long time, UUID uuid, String name, int uid, Location location, boolean inc) { diff --git a/src/dev/heliosares/auxprotect/utils/PosEncoder.java b/src/dev/heliosares/auxprotect/utils/PosEncoder.java index 5c9d934..8ec1868 100644 --- a/src/dev/heliosares/auxprotect/utils/PosEncoder.java +++ b/src/dev/heliosares/auxprotect/utils/PosEncoder.java @@ -177,9 +177,15 @@ public class PosEncoder { * X and Z are stored in 8ths, Y is stored in 4ths. */ public static byte getFractionalByte(double dx, double dy, double dz) { - int x = (int) Math.min(Math.round((Math.abs(dx % 1)) * 8), 7) << 5; - int y = (int) Math.min(Math.round((Math.abs(dy % 1)) * 4), 3) << 3; - int z = (int) Math.min(Math.round((Math.abs(dz % 1)) * 8), 7); + dx %= 1; + dy %= 1; + dz %= 1; + if (dx < 0) dx++; + if (dy < 0) dy++; + if (dz < 0) dz++; + int x = (int) Math.min(Math.round(dx * 8), 7) << 5; + int y = (int) Math.min(Math.round(dy * 4), 3) << 3; + int z = (int) Math.min(Math.round(dz * 8), 7); return (byte) (x | y | z); } diff --git a/src/dev/heliosares/auxprotect/utils/TimeUtil.java b/src/dev/heliosares/auxprotect/utils/TimeUtil.java index bda3c85..57185fa 100644 --- a/src/dev/heliosares/auxprotect/utils/TimeUtil.java +++ b/src/dev/heliosares/auxprotect/utils/TimeUtil.java @@ -1,7 +1,13 @@ package dev.heliosares.auxprotect.utils; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + public class TimeUtil { + public static final DateTimeFormatter entryTimeFormat = DateTimeFormatter.ofPattern("ddMMMyy HH:mm:ss.SSS"); + private static String padDouble(double d) { String[] split = (d + "").split("\\."); while (split[0].length() < 2) { @@ -109,4 +115,8 @@ public class TimeUtil { } return time; } + + public static String format(long millis, DateTimeFormatter formatter) { + return Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).format(formatter); + } }