From 88cf021c3b4e13b8e144d408fe21f817f705682f Mon Sep 17 00:00:00 2001 From: Xelara Networks Date: Sat, 20 Jun 2026 12:05:20 -0400 Subject: [PATCH] added --- README.md | 0 pom.xml | 48 +++++ .../bitnix/dirtspy/ClientBrandListener.java | 119 +++++++++++++ .../com/bitnix/dirtspy/DirtSpyCommand.java | 124 +++++++++++++ .../com/bitnix/dirtspy/DirtSpyPlugin.java | 93 ++++++++++ .../com/bitnix/dirtspy/PlayerDataManager.java | 146 ++++++++++++++++ .../com/bitnix/dirtspy/PlayerListener.java | 130 ++++++++++++++ .../java/com/bitnix/dirtspy/PlayerRecord.java | 165 ++++++++++++++++++ .../java/com/bitnix/dirtspy/SaveTask.java | 19 ++ src/main/resources/config.yml | 35 ++++ src/main/resources/plugin.yml | 25 +++ target/DirtSpy-1.0-SNAPSHOT.jar | Bin 0 -> 20450 bytes .../ClientBrandListener$VarIntResult.class | Bin 0 -> 565 bytes .../bitnix/dirtspy/ClientBrandListener.class | Bin 0 -> 3827 bytes .../com/bitnix/dirtspy/DirtSpyCommand.class | Bin 0 -> 7466 bytes .../com/bitnix/dirtspy/DirtSpyPlugin.class | Bin 0 -> 4164 bytes .../bitnix/dirtspy/PlayerDataManager.class | Bin 0 -> 9168 bytes .../com/bitnix/dirtspy/PlayerListener.class | Bin 0 -> 5744 bytes .../com/bitnix/dirtspy/PlayerRecord.class | Bin 0 -> 3767 bytes .../classes/com/bitnix/dirtspy/SaveTask.class | Bin 0 -> 722 bytes target/classes/config.yml | 35 ++++ target/classes/plugin.yml | 25 +++ target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 8 + .../compile/default-compile/inputFiles.lst | 7 + 25 files changed, 984 insertions(+) create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/bitnix/dirtspy/ClientBrandListener.java create mode 100644 src/main/java/com/bitnix/dirtspy/DirtSpyCommand.java create mode 100644 src/main/java/com/bitnix/dirtspy/DirtSpyPlugin.java create mode 100644 src/main/java/com/bitnix/dirtspy/PlayerDataManager.java create mode 100644 src/main/java/com/bitnix/dirtspy/PlayerListener.java create mode 100644 src/main/java/com/bitnix/dirtspy/PlayerRecord.java create mode 100644 src/main/java/com/bitnix/dirtspy/SaveTask.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 target/DirtSpy-1.0-SNAPSHOT.jar create mode 100644 target/classes/com/bitnix/dirtspy/ClientBrandListener$VarIntResult.class create mode 100644 target/classes/com/bitnix/dirtspy/ClientBrandListener.class create mode 100644 target/classes/com/bitnix/dirtspy/DirtSpyCommand.class create mode 100644 target/classes/com/bitnix/dirtspy/DirtSpyPlugin.class create mode 100644 target/classes/com/bitnix/dirtspy/PlayerDataManager.class create mode 100644 target/classes/com/bitnix/dirtspy/PlayerListener.class create mode 100644 target/classes/com/bitnix/dirtspy/PlayerRecord.class create mode 100644 target/classes/com/bitnix/dirtspy/SaveTask.class create mode 100644 target/classes/config.yml create mode 100644 target/classes/plugin.yml create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6e28d7d --- /dev/null +++ b/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + com.bitnix + DirtSpy + 1.0-SNAPSHOT + jar + + DirtSpy + Admin review plugin for collecting realistic player/client/network stats on Paper. + + + 21 + 21 + UTF-8 + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.21.1-R0.1-SNAPSHOT + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 21 + + + + + diff --git a/src/main/java/com/bitnix/dirtspy/ClientBrandListener.java b/src/main/java/com/bitnix/dirtspy/ClientBrandListener.java new file mode 100644 index 0000000..8701009 --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/ClientBrandListener.java @@ -0,0 +1,119 @@ +package com.bitnix.dirtspy; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import java.nio.charset.StandardCharsets; + +public class ClientBrandListener implements PluginMessageListener { + + private final DirtSpyPlugin plugin; + + public ClientBrandListener(DirtSpyPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + if (player == null || message == null) { + return; + } + + if (!"minecraft:brand".equals(channel)) { + return; + } + + if (!plugin.getConfig().getBoolean("settings.track-client-brand", true)) { + return; + } + + String brand = decodeBrand(message); + PlayerRecord record = plugin.getPlayerDataManager().getOrCreate(player.getUniqueId()); + record.setName(player.getName()); + record.setClientBrand(brand); + record.setLastSeen(System.currentTimeMillis()); + } + + private String decodeBrand(byte[] data) { + try { + VarIntResult lenResult = readVarInt(data, 0); + if (lenResult == null) { + return sanitize(fallbackDecode(data)); + } + + int length = lenResult.value; + int start = lenResult.nextIndex; + + if (length < 0 || start < 0 || start + length > data.length) { + return sanitize(fallbackDecode(data)); + } + + String decoded = new String(data, start, length, StandardCharsets.UTF_8); + return sanitize(decoded); + } catch (Throwable ignored) { + return sanitize(fallbackDecode(data)); + } + } + + private String fallbackDecode(byte[] data) { + try { + return new String(data, StandardCharsets.UTF_8); + } catch (Throwable ignored) { + return "unknown"; + } + } + + private String sanitize(String input) { + if (input == null) { + return "unknown"; + } + + String cleaned = input + .replace("\u0000", "") + .replaceAll("\\p{Cntrl}", "") + .trim(); + + if (cleaned.isEmpty()) { + return "unknown"; + } + + if (cleaned.length() > 64) { + cleaned = cleaned.substring(0, 64); + } + + return cleaned; + } + + private VarIntResult readVarInt(byte[] data, int offset) { + int numRead = 0; + int result = 0; + byte read; + + do { + if (offset + numRead >= data.length) { + return null; + } + + read = data[offset + numRead]; + int value = read & 0b01111111; + result |= (value << (7 * numRead)); + + numRead++; + if (numRead > 5) { + return null; + } + } while ((read & 0b10000000) != 0); + + return new VarIntResult(result, offset + numRead); + } + + private static class VarIntResult { + private final int value; + private final int nextIndex; + + private VarIntResult(int value, int nextIndex) { + this.value = value; + this.nextIndex = nextIndex; + } + } +} diff --git a/src/main/java/com/bitnix/dirtspy/DirtSpyCommand.java b/src/main/java/com/bitnix/dirtspy/DirtSpyCommand.java new file mode 100644 index 0000000..8920f1b --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/DirtSpyCommand.java @@ -0,0 +1,124 @@ +package com.bitnix.dirtspy; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class DirtSpyCommand implements CommandExecutor, TabCompleter { + + private final DirtSpyPlugin plugin; + + public DirtSpyCommand(DirtSpyPlugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("dirtspy.use")) { + sender.sendMessage(plugin.message("messages.no-permission")); + return true; + } + + if (args.length != 1) { + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.usage")); + return true; + } + + if (args[0].equalsIgnoreCase("reload")) { + if (!sender.hasPermission("dirtspy.reload")) { + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.no-permission")); + return true; + } + + plugin.reloadPlugin(); + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.reloaded")); + return true; + } + + if (!sender.hasPermission("dirtspy.view")) { + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.no-permission")); + return true; + } + + String targetName = args[0]; + PlayerRecord record = plugin.getPlayerDataManager().getByName(targetName); + + if (record == null) { + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayerIfCached(targetName); + if (offlinePlayer != null && offlinePlayer.getUniqueId() != null) { + record = plugin.getPlayerDataManager().getOrCreate(offlinePlayer.getUniqueId()); + if (record.getName() == null) { + record.setName(offlinePlayer.getName() == null ? targetName : offlinePlayer.getName()); + } + } + } + + if (record == null || record.getName() == null) { + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.player-not-found")); + return true; + } + + List alts = plugin.getPlayerDataManager().getAltNamesOnIp(record.getLastIp(), record.getUuid()); + + sender.sendMessage(plugin.message("messages.prefix") + plugin.message("messages.header").replace("%player%", record.getName())); + sender.sendMessage(format("messages.line-name", safe(record.getName()))); + sender.sendMessage(format("messages.line-uuid", String.valueOf(record.getUuid()))); + sender.sendMessage(format("messages.line-first-seen", formatTime(record.getFirstSeen()))); + sender.sendMessage(format("messages.line-last-seen", formatTime(record.getLastSeen()))); + sender.sendMessage(format("messages.line-joins", String.valueOf(record.getJoinCount()))); + sender.sendMessage(format("messages.line-current-ip", safe(record.getCurrentIp()))); + sender.sendMessage(format("messages.line-last-ip", safe(record.getLastIp()))); + sender.sendMessage(format("messages.line-locale", safe(record.getLocale()))); + sender.sendMessage(format("messages.line-brand", safe(record.getClientBrand()))); + sender.sendMessage(format("messages.line-view-distance", String.valueOf(record.getClientViewDistance()))); + sender.sendMessage(format("messages.line-last-world", safe(record.getLastWorld()))); + sender.sendMessage(format("messages.line-last-gamemode", safe(record.getLastGamemode()))); + sender.sendMessage(format("messages.line-last-join", formatTime(record.getLastJoinTime()))); + sender.sendMessage(format("messages.line-last-quit", formatTime(record.getLastQuitTime()))); + sender.sendMessage(format("messages.line-total-playtime", String.valueOf(record.getTotalTrackedPlaytimeSeconds()))); + sender.sendMessage(format("messages.line-alt-count", String.valueOf(alts.size()))); + sender.sendMessage(format("messages.line-alts", alts.isEmpty() ? "none" : String.join(", ", alts))); + sender.sendMessage(format("messages.line-online", String.valueOf(record.isOnline()))); + return true; + } + + private String format(String path, String value) { + return plugin.message("messages.prefix") + plugin.message(path).replace("%value%", value); + } + + private String safe(String input) { + return input == null || input.isBlank() ? "unknown" : input; + } + + private String formatTime(long millis) { + if (millis <= 0L) { + return "never"; + } + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(millis)); + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + List completions = new ArrayList<>(); + if (args.length == 1) { + if ("reload".startsWith(args[0].toLowerCase())) { + completions.add("reload"); + } + + Bukkit.getOnlinePlayers().forEach(player -> { + if (player.getName().toLowerCase().startsWith(args[0].toLowerCase())) { + completions.add(player.getName()); + } + }); + } + return completions; + } +} diff --git a/src/main/java/com/bitnix/dirtspy/DirtSpyPlugin.java b/src/main/java/com/bitnix/dirtspy/DirtSpyPlugin.java new file mode 100644 index 0000000..807b2b4 --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/DirtSpyPlugin.java @@ -0,0 +1,93 @@ +package com.bitnix.dirtspy; + +import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; + +public class DirtSpyPlugin extends JavaPlugin { + + private PlayerDataManager playerDataManager; + private SaveTask saveTask; + private ClientBrandListener clientBrandListener; + + @Override + public void onEnable() { + saveDefaultConfig(); + + this.playerDataManager = new PlayerDataManager(this); + this.playerDataManager.load(); + + DirtSpyCommand commandExecutor = new DirtSpyCommand(this); + PluginCommand command = getCommand("dirtspy"); + if (command != null) { + command.setExecutor(commandExecutor); + command.setTabCompleter(commandExecutor); + } else { + getLogger().severe("Command 'dirtspy' is missing from plugin.yml"); + } + + Bukkit.getPluginManager().registerEvents(new PlayerListener(this), this); + + this.clientBrandListener = new ClientBrandListener(this); + getServer().getMessenger().registerIncomingPluginChannel(this, "minecraft:brand", clientBrandListener); + + long intervalSeconds = getConfig().getLong("settings.save-interval-seconds", 300L); + if (intervalSeconds > 0) { + this.saveTask = new SaveTask(this); + this.saveTask.runTaskTimer(this, intervalSeconds * 20L, intervalSeconds * 20L); + } + + getLogger().info("DirtSpy enabled."); + } + + @Override + public void onDisable() { + if (saveTask != null) { + saveTask.cancel(); + saveTask = null; + } + + if (clientBrandListener != null) { + getServer().getMessenger().unregisterIncomingPluginChannel(this, "minecraft:brand", clientBrandListener); + clientBrandListener = null; + } + + if (getConfig().getBoolean("settings.save-on-disable", true) && playerDataManager != null) { + playerDataManager.save(); + } + + if (playerDataManager != null) { + playerDataManager.clear(); + playerDataManager = null; + } + + getLogger().info("DirtSpy disabled."); + } + + public PlayerDataManager getPlayerDataManager() { + return playerDataManager; + } + + public String color(String input) { + return input == null ? "" : input.replace("&", "ยง"); + } + + public String message(String path) { + return color(getConfig().getString(path, "")); + } + + public void reloadPlugin() { + reloadConfig(); + + if (saveTask != null) { + saveTask.cancel(); + saveTask = null; + } + + long intervalSeconds = getConfig().getLong("settings.save-interval-seconds", 300L); + if (intervalSeconds > 0) { + this.saveTask = new SaveTask(this); + this.saveTask.runTaskTimer(this, intervalSeconds * 20L, intervalSeconds * 20L); + } + } +} diff --git a/src/main/java/com/bitnix/dirtspy/PlayerDataManager.java b/src/main/java/com/bitnix/dirtspy/PlayerDataManager.java new file mode 100644 index 0000000..8d58ead --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/PlayerDataManager.java @@ -0,0 +1,146 @@ +package com.bitnix.dirtspy; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class PlayerDataManager { + + private final DirtSpyPlugin plugin; + private final File dataFile; + private final Map records = new HashMap<>(); + + public PlayerDataManager(DirtSpyPlugin plugin) { + this.plugin = plugin; + this.dataFile = new File(plugin.getDataFolder(), "players.yml"); + } + + public void load() { + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdirs(); + } + + if (!dataFile.exists()) { + try { + dataFile.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Could not create players.yml: " + e.getMessage()); + } + } + + YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile); + ConfigurationSection playersSection = config.getConfigurationSection("players"); + if (playersSection == null) { + return; + } + + for (String key : playersSection.getKeys(false)) { + try { + UUID uuid = UUID.fromString(key); + ConfigurationSection section = playersSection.getConfigurationSection(key); + if (section == null) { + continue; + } + + PlayerRecord record = new PlayerRecord(uuid); + record.setName(section.getString("name", "unknown")); + record.setFirstSeen(section.getLong("first-seen", 0L)); + record.setLastSeen(section.getLong("last-seen", 0L)); + record.setJoinCount(section.getInt("join-count", 0)); + record.setCurrentIp(section.getString("current-ip", "unknown")); + record.setLastIp(section.getString("last-ip", "unknown")); + record.setLocale(section.getString("locale", "unknown")); + record.setClientBrand(section.getString("client-brand", "unknown")); + record.setClientViewDistance(section.getInt("client-view-distance", -1)); + record.setLastWorld(section.getString("last-world", "unknown")); + record.setLastGamemode(section.getString("last-gamemode", "unknown")); + record.setLastJoinTime(section.getLong("last-join-time", 0L)); + record.setLastQuitTime(section.getLong("last-quit-time", 0L)); + record.setTotalTrackedPlaytimeSeconds(section.getLong("total-tracked-playtime-seconds", 0L)); + record.setOnline(section.getBoolean("online", false)); + + records.put(uuid, record); + } catch (IllegalArgumentException ex) { + plugin.getLogger().warning("Skipping invalid UUID in players.yml: " + key); + } + } + } + + public void save() { + YamlConfiguration config = new YamlConfiguration(); + + for (Map.Entry entry : records.entrySet()) { + String base = "players." + entry.getKey(); + PlayerRecord record = entry.getValue(); + + config.set(base + ".name", record.getName()); + config.set(base + ".first-seen", record.getFirstSeen()); + config.set(base + ".last-seen", record.getLastSeen()); + config.set(base + ".join-count", record.getJoinCount()); + config.set(base + ".current-ip", record.getCurrentIp()); + config.set(base + ".last-ip", record.getLastIp()); + config.set(base + ".locale", record.getLocale()); + config.set(base + ".client-brand", record.getClientBrand()); + config.set(base + ".client-view-distance", record.getClientViewDistance()); + config.set(base + ".last-world", record.getLastWorld()); + config.set(base + ".last-gamemode", record.getLastGamemode()); + config.set(base + ".last-join-time", record.getLastJoinTime()); + config.set(base + ".last-quit-time", record.getLastQuitTime()); + config.set(base + ".total-tracked-playtime-seconds", record.getTotalTrackedPlaytimeSeconds()); + config.set(base + ".online", record.isOnline()); + } + + try { + config.save(dataFile); + } catch (IOException e) { + plugin.getLogger().severe("Could not save players.yml: " + e.getMessage()); + } + } + + public PlayerRecord getOrCreate(UUID uuid) { + return records.computeIfAbsent(uuid, PlayerRecord::new); + } + + public PlayerRecord getByName(String name) { + for (PlayerRecord record : records.values()) { + if (record.getName() != null && record.getName().equalsIgnoreCase(name)) { + return record; + } + } + return null; + } + + public List getAltNamesOnIp(String ip, UUID exclude) { + List matching = new ArrayList<>(); + for (Map.Entry entry : records.entrySet()) { + if (entry.getKey().equals(exclude)) { + continue; + } + + PlayerRecord record = entry.getValue(); + if (record.getLastIp() != null && record.getLastIp().equalsIgnoreCase(ip)) { + matching.add(record); + } + } + + matching.sort(Comparator.comparing(record -> record.getName() == null ? "" : record.getName(), String.CASE_INSENSITIVE_ORDER)); + + List names = new ArrayList<>(); + for (PlayerRecord record : matching) { + names.add(record.getName() == null ? "unknown" : record.getName()); + } + return names; + } + + public void clear() { + records.clear(); + } +} diff --git a/src/main/java/com/bitnix/dirtspy/PlayerListener.java b/src/main/java/com/bitnix/dirtspy/PlayerListener.java new file mode 100644 index 0000000..00c74c0 --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/PlayerListener.java @@ -0,0 +1,130 @@ +package com.bitnix.dirtspy; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLocaleChangeEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.net.InetSocketAddress; + +public class PlayerListener implements Listener { + + private final DirtSpyPlugin plugin; + + public PlayerListener(DirtSpyPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + PlayerRecord record = plugin.getPlayerDataManager().getOrCreate(player.getUniqueId()); + + long now = System.currentTimeMillis(); + + record.setName(player.getName()); + if (record.getFirstSeen() <= 0L) { + record.setFirstSeen(now); + } + record.setLastSeen(now); + record.setJoinCount(record.getJoinCount() + 1); + record.setLastJoinTime(now); + record.setOnline(true); + + if (plugin.getConfig().getBoolean("settings.track-ip-address", true)) { + InetSocketAddress address = player.getAddress(); + String ip = (address != null && address.getAddress() != null) + ? address.getAddress().getHostAddress() + : "unknown"; + record.setCurrentIp(ip); + record.setLastIp(ip); + } + + if (plugin.getConfig().getBoolean("settings.track-locale", true)) { + try { + record.setLocale(player.locale().toString()); + } catch (Throwable ignored) { + record.setLocale("unknown"); + } + } + + if (plugin.getConfig().getBoolean("settings.track-client-view-distance", true)) { + try { + record.setClientViewDistance(player.getClientViewDistance()); + } catch (Throwable ignored) { + record.setClientViewDistance(-1); + } + } + + try { + record.setLastWorld(player.getWorld().getName()); + } catch (Throwable ignored) { + record.setLastWorld("unknown"); + } + + GameMode gameMode = player.getGameMode(); + record.setLastGamemode(gameMode == null ? "unknown" : gameMode.name()); + + if (plugin.getConfig().getBoolean("settings.log-joins-to-console", true)) { + plugin.getLogger().info("Tracked join for " + player.getName() + + " | IP=" + record.getLastIp() + + " | locale=" + record.getLocale() + + " | vd=" + record.getClientViewDistance()); + } + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + PlayerRecord record = plugin.getPlayerDataManager().getOrCreate(player.getUniqueId()); + + long now = System.currentTimeMillis(); + + record.setName(player.getName()); + record.setLastSeen(now); + record.setLastQuitTime(now); + record.setOnline(false); + + if (record.getLastJoinTime() > 0L && now >= record.getLastJoinTime()) { + long seconds = (now - record.getLastJoinTime()) / 1000L; + record.setTotalTrackedPlaytimeSeconds(record.getTotalTrackedPlaytimeSeconds() + seconds); + } + + record.setCurrentIp("offline"); + + try { + record.setLastWorld(player.getWorld().getName()); + } catch (Throwable ignored) { + record.setLastWorld("unknown"); + } + + GameMode gameMode = player.getGameMode(); + record.setLastGamemode(gameMode == null ? "unknown" : gameMode.name()); + } + + @EventHandler + public void onLocaleChange(PlayerLocaleChangeEvent event) { + if (!plugin.getConfig().getBoolean("settings.track-locale", true)) { + return; + } + + PlayerRecord record = plugin.getPlayerDataManager().getOrCreate(event.getPlayer().getUniqueId()); + record.setLocale(String.valueOf(event.locale())); + record.setLastSeen(System.currentTimeMillis()); + } + + @EventHandler + public void onWorldChange(PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + PlayerRecord record = plugin.getPlayerDataManager().getOrCreate(player.getUniqueId()); + record.setLastWorld(player.getWorld().getName()); + record.setLastSeen(System.currentTimeMillis()); + + GameMode gameMode = player.getGameMode(); + record.setLastGamemode(gameMode == null ? "unknown" : gameMode.name()); + } +} diff --git a/src/main/java/com/bitnix/dirtspy/PlayerRecord.java b/src/main/java/com/bitnix/dirtspy/PlayerRecord.java new file mode 100644 index 0000000..287e512 --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/PlayerRecord.java @@ -0,0 +1,165 @@ +package com.bitnix.dirtspy; + +import java.util.UUID; + +public class PlayerRecord { + + private final UUID uuid; + private String name; + private long firstSeen; + private long lastSeen; + private int joinCount; + private String currentIp; + private String lastIp; + private String locale; + private String clientBrand; + private int clientViewDistance; + private String lastWorld; + private String lastGamemode; + private long lastJoinTime; + private long lastQuitTime; + private long totalTrackedPlaytimeSeconds; + private boolean online; + + public PlayerRecord(UUID uuid) { + this.uuid = uuid; + this.firstSeen = 0L; + this.lastSeen = 0L; + this.joinCount = 0; + this.currentIp = "unknown"; + this.lastIp = "unknown"; + this.locale = "unknown"; + this.clientBrand = "unknown"; + this.clientViewDistance = -1; + this.lastWorld = "unknown"; + this.lastGamemode = "unknown"; + this.lastJoinTime = 0L; + this.lastQuitTime = 0L; + this.totalTrackedPlaytimeSeconds = 0L; + this.online = false; + } + + public UUID getUuid() { + return uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getFirstSeen() { + return firstSeen; + } + + public void setFirstSeen(long firstSeen) { + this.firstSeen = firstSeen; + } + + public long getLastSeen() { + return lastSeen; + } + + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + } + + public int getJoinCount() { + return joinCount; + } + + public void setJoinCount(int joinCount) { + this.joinCount = joinCount; + } + + public String getCurrentIp() { + return currentIp; + } + + public void setCurrentIp(String currentIp) { + this.currentIp = currentIp; + } + + public String getLastIp() { + return lastIp; + } + + public void setLastIp(String lastIp) { + this.lastIp = lastIp; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public String getClientBrand() { + return clientBrand; + } + + public void setClientBrand(String clientBrand) { + this.clientBrand = clientBrand; + } + + public int getClientViewDistance() { + return clientViewDistance; + } + + public void setClientViewDistance(int clientViewDistance) { + this.clientViewDistance = clientViewDistance; + } + + public String getLastWorld() { + return lastWorld; + } + + public void setLastWorld(String lastWorld) { + this.lastWorld = lastWorld; + } + + public String getLastGamemode() { + return lastGamemode; + } + + public void setLastGamemode(String lastGamemode) { + this.lastGamemode = lastGamemode; + } + + public long getLastJoinTime() { + return lastJoinTime; + } + + public void setLastJoinTime(long lastJoinTime) { + this.lastJoinTime = lastJoinTime; + } + + public long getLastQuitTime() { + return lastQuitTime; + } + + public void setLastQuitTime(long lastQuitTime) { + this.lastQuitTime = lastQuitTime; + } + + public long getTotalTrackedPlaytimeSeconds() { + return totalTrackedPlaytimeSeconds; + } + + public void setTotalTrackedPlaytimeSeconds(long totalTrackedPlaytimeSeconds) { + this.totalTrackedPlaytimeSeconds = totalTrackedPlaytimeSeconds; + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) { + this.online = online; + } +} diff --git a/src/main/java/com/bitnix/dirtspy/SaveTask.java b/src/main/java/com/bitnix/dirtspy/SaveTask.java new file mode 100644 index 0000000..476406b --- /dev/null +++ b/src/main/java/com/bitnix/dirtspy/SaveTask.java @@ -0,0 +1,19 @@ +package com.bitnix.dirtspy; + +import org.bukkit.scheduler.BukkitRunnable; + +public class SaveTask extends BukkitRunnable { + + private final DirtSpyPlugin plugin; + + public SaveTask(DirtSpyPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + if (plugin.getPlayerDataManager() != null) { + plugin.getPlayerDataManager().save(); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..90b36cc --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,35 @@ +settings: + save-on-disable: true + save-interval-seconds: 300 + track-ip-address: true + track-locale: true + track-client-view-distance: true + track-client-brand: true + log-joins-to-console: true + +messages: + prefix: "&8[&6DirtSpy&8] " + no-permission: "&cYou do not have permission." + player-only: "&cOnly players can use this command." + usage: "&eUsage: /dirtspy " + reloaded: "&aDirtSpy config reloaded." + player-not-found: "&cPlayer not found." + header: "&6DirtSpy report for &e%player%" + line-name: "&7Name: &f%value%" + line-uuid: "&7UUID: &f%value%" + line-first-seen: "&7First Seen: &f%value%" + line-last-seen: "&7Last Seen: &f%value%" + line-joins: "&7Join Count: &f%value%" + line-current-ip: "&7Current IP: &f%value%" + line-last-ip: "&7Last IP: &f%value%" + line-locale: "&7Locale: &f%value%" + line-brand: "&7Client Brand: &f%value%" + line-view-distance: "&7Client View Distance: &f%value%" + line-last-world: "&7Last World: &f%value%" + line-last-gamemode: "&7Last Gamemode: &f%value%" + line-last-join: "&7Last Join: &f%value%" + line-last-quit: "&7Last Quit: &f%value%" + line-total-playtime: "&7Tracked Playtime: &f%value% sec" + line-alt-count: "&7Accounts on Last IP: &f%value%" + line-alts: "&7Alt Names: &f%value%" + line-online: "&7Online: &f%value%" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..47aefa6 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,25 @@ +name: DirtSpy +version: 1.0-SNAPSHOT +main: com.bitnix.dirtspy.DirtSpyPlugin +api-version: '1.21' +author: bitnix +description: Collects realistic client, connection, and behavior stats for admin review. + +commands: + dirtspy: + description: View DirtSpy info and reload the plugin + usage: /dirtspy + permission: dirtspy.use + +permissions: + dirtspy.use: + description: Allows use of DirtSpy commands + default: op + + dirtspy.reload: + description: Allows reloading DirtSpy config + default: op + + dirtspy.view: + description: Allows viewing collected DirtSpy data + default: op diff --git a/target/DirtSpy-1.0-SNAPSHOT.jar b/target/DirtSpy-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..9c0038b8dbd22807ba4198579b2d932a837b882c GIT binary patch literal 20450 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/ClientBrandListener$VarIntResult.class b/target/classes/com/bitnix/dirtspy/ClientBrandListener$VarIntResult.class new file mode 100644 index 0000000000000000000000000000000000000000..9333633ad104aad4ff34fab7ee990efd9bd0ff2f GIT binary patch literal 565 zcmbVJO-n*S6g}hD)7MMQzBCP7M5v42B3cze3DE;Fs1RC?I@HK>Uc8wn_On_@(4rsE zkBaVCixw@S&76A=_ntFz=KbUK4Zs<8U8In9knxa3j-fo^Gwz36-TPO=i5MA%+=*1u zoHC?a?Lh%~6dbr7R^Txlj-shQlt#(9KbEo4k57I#ltP*Fn5%J5Y9o}0_Xa!;l(`l< z2}v)TOYE>Jn?H6S`!aM-2_QpK!x}xBvhE literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/ClientBrandListener.class b/target/classes/com/bitnix/dirtspy/ClientBrandListener.class new file mode 100644 index 0000000000000000000000000000000000000000..6e74ab60eddfe685174d92b292ac2adc5e8cd380 GIT binary patch literal 3827 zcmb7HX?GLX8Gf!c^2lNgLLd_x3}y+DEgOY67-|~_YzGjvfOxAhiIcH3wg-0sjvYeCCPvxAR{*?9~kfzU_k!?AMr2XL0-QMlF&-=dD|M<_l z?*llFcf+VbK!u_qh+2WjMg6j#v~+tmd3O4uk#PlTdrjMP`vd~<*6})oP^TiSVFNUQ z{h53&Ic>VOxtPqFj$2q*N)A}2VY~f~ZfA$gf@|1@LnRAVan`g2_6$`K8KmgQ!qPeK zOiu_K1vceO+sHWjjN3geRfQ43W))ElTd;MVpChhg+OsraTq^2TK_C_%`e+nAt&?Fi zV4I3ZG&G_~pyiX?5U86q+=0A3V^Vo%ymct=%qFLc^Yf;g%y`R1M|aJ+jZ8jkcy}WOF7&rnKvkBJK*cq*qD^2c4V<&|CBqriT|KSa z`mDUoIMJwr-vcLmHlQ6HDh_B!;&FjTDk3CDOm`{i%g{Ai8newyMI)6Jh|+*}heg-4 zl4E13!JaUlK&Of)H5@{hz`p;*zOVMIGvFAyYsg#tsMe2(W0+VuSucbm8a{AO?aquNH4e}hCv1? z@zc^~2xm2%!!zU;am$YDPReE-=q6oKRq?8DYbE3N`BKA1p=i0hyAVb+j7kn}oY5_7 znyE18aZzrKYxoQ%1VRO!mBxIL`0lSl<|x)-TZLYsQ{+17CtmcW*^vNk&BwV%5c}epVs6@SM&wZ-dI8;r(3OehH867FOy2Fa7 z`t6husphPmcO+P`if~txlzu2#Hc%BeDQ(&dMJb(;eOt=Z=4WPDN4R7cbHgmTUW~GG zQcjj&)+WXu9?2J-j3FNyY^7EFJRo_>qJG9GxM@RDgcQ(Hya#8%(hCJcPAdyD-yI>o z{{+6vzo>lD@ljaIaS-3<_XnJv;C>B%!)d|9+cbB`a}tvu5;9O2Wkni=ys)qW^bXR zguUI0zy4VJJ4gtWaIibr806%zob)sX8+rVAWAJSZmT+2n81OuN!iy-%D7VnV2FAFJ zqbBUdcI?AWB(Mud(ZU|O8)vbHZD_9-MW65AY7`Ur34TiN=`((YpL;P*;1{?_I|+2) zm)upTX*+&}TSV{IJXs5cZ{-Nj^6lkEF5+C3kx#WH0wtVDx8Fm3TN`5^E@5oB{SMCe z@$-C_(tHD(W6BL^F{Sy1#QV1@kG+S;`2^SB#PnUvbqCuTgHk%ZrpUXOtr)>PV(9UH zdok?&`nVk6tpJZfMVzBno@ys59Yi9@XV&9GA-go?uDIOPDdA(y&IAIn&L80J53_pw7oL=ugUwzBL0<|U|SbGi*Kz3W?8IMjhbuC>Gz31Nv!d=k_OgZJEw0tD7To2pLJ%hLHb-G^6v8_Q{})*U By&?br literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/DirtSpyCommand.class b/target/classes/com/bitnix/dirtspy/DirtSpyCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..b4342ce4987396b026497390fb1813082a2afbd8 GIT binary patch literal 7466 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 BeS-i1 literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/PlayerDataManager.class b/target/classes/com/bitnix/dirtspy/PlayerDataManager.class new file mode 100644 index 0000000000000000000000000000000000000000..49ca177913f3a62698f6b6c0ba44a0eecd7bcf99 GIT binary patch literal 9168 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 Xlt#axyvAF+-&a-3ALMoU6YBpLz^}jA literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/PlayerListener.class b/target/classes/com/bitnix/dirtspy/PlayerListener.class new file mode 100644 index 0000000000000000000000000000000000000000..c3ca81786912b88ebf0503bc5b8f2c3a233ada17 GIT binary patch literal 5744 zcmbVQ349dQ8UMdzv$F}qVSq?LLllq%vaCWXNi@vWHdh|K`nZCdqRAN`5;t@4fGR=l}cO z_juyqqn`)RF7z5ypi)CfM-{>XGjUj=Op@ zqnkaNwi-;sbPY3f9EVzg#ZERA+nFC4vfP+CV%lyj<1zPTbUBuNuACOAPMNL}EO0_o zb9Zo~?OJY8ElQY$*&6C}9FI8yjYm^SU{;CsI>R-34BJRavzjQS%x0xTfU<20=3<_P z6Lg%227x(;Q%ztBC2h5>%kySDDNsuRUNrKqm5y!Q8ed1{8*!3`lXWb>LV<I+m^#cVn&ttm*UegXib77sX&RR6SblNF;a;Pb;*mmlOOSaQ}wvtBrOzu5TM>l$iWv*?q z)0Rz7g_^d@jkB;x$7Y9@Z8|Q%g#xhvSdWFhKn)$e)^XCN!Hlmf6PNAH z+a)M2(s3~^QS|Lfwm?G>7q;of;(YWvi6PVV*NKoFxJ-i~x9${}>t#n-Ql{BNR%1+( zFf|P5NJ&u68pouh$uyeL>Ew!{Be00Kygg()yFK^}=}1eIO89gr=*KfMh_((#F32EC z+{7~xTrMzsg2&TN!bqFa5jh<$@+^~TDv%sr-gkS1G<`R&&@igwwNh(UrUL{55!X?P zB(=Co$JHe*JPsPJ6_`0z-TMZ!&TeC8+Kk|Ofdv!POQbC(b9BTqcSn<~7EXcrk^D1diCi*Y9LXc;~MEcS@_JPJg!~V-(DVcc9P}qRD4CpSEZ~5hVuxd5j@6{;0z4NLS2K$ zrBz=aUop$HEGnHl2H9YlzIBg-VxPn}HGE6QxAC1~b_bkHK_f;wZ*Ce8I3*Z6)uXkQ z4a?I~qwgM7Bc)5yOCL`o4d3Sp5y%5&##lqQsHt4zGro~)?q|F^yv&>4O$Tkv5AQUy zeX`)Nv+=ypZ)7bw_9sK`pllizl;f?Ms-gyrP1vzzm<3vzf`y}u!zGz#R-05wGm1ye z2@2h1PY(*HurgB5v3lt@DP&=#Y$t27YRwKTm&z&kR(Wa&jH_u!RLyIWW!KC@ZJa&h zT31qKy?%^+_u;+dw`^=lcvcCBE?H?yWpH+oeX+n%=c&Ce^P$Jc_|d4}!nTHa(QoA} z#;D!4nKZJmV2I`ic__O)4|f`NGHqs6cw~kiLwHJAuELX3X?S$sjqMYd=Geis5@Ynt|Cfk(>UN zWPzD+8~a8xW*PE}0`z4HqR*nkNT+)(*CagkxXqhsN7~5c%$z170*fZuR8Ccmu0X?) zNtvX|E}X=xas>pw$6u|yF7q&|;&&CE;qwO^t>=0L-!)4ff>^TjL1_Cqs=yEVoQ6t1 zYZ1asRI_#X5l3~38b8KQIO0B!D{~w_#m~4#o)yoNe*_R~mliO!XUPN53YgjQ2-F1rYcu9AIqlvGQ{5uhg(12x_k55Sw^~-hsuubTqNwZ^pG) zf<0J@8_|NB5Tm5iD65symSaC}zz^}>dH`oq?rKV2Ma;BO`Z<)}K@HmR0@mV1-Zfug zvpoZyq7iYi3|(S5&J(AjTb#vlJ2rZ_$q*+}?$7ZHgeg_WFYzmAloQ6U@f$v$p$+Qm zh0+``R74uTp2cq^cEuX}4!O>2k!yo90d15iO`XiD03ibIDwF~1f^vh+KdJwG| z=4v=K^fJ0M>}c2UkL&G)N#e9U%mtThn^RR}9hG{;v#-?0w+aWTYyi&mxGFm|& zfU*Ss%D2GZ@OPC1g7A|$fIFE34;(!QB=lE%P=6>|NvIP9k5=!!3ybPP@^kO)n5VwX zS@_)W%CO9jFVuyN0=^ur{hCDh|AWWLpT&p?L%rI@2X zUAFjnCfA7PqedmC&LD~P{zrM0BkcGT!bW)9{CZlZv zIHF2Jzfgsu^`TOsA`E4r8dJW3ISO9*s~EgGTt%g>X0^DMRpL5qV7TR((5hsY4Ctf* zFL?p&C!Ni-iP%dhf_#c(1z{^a25N|@rR6Ho8 O*K0V^M75ZL`d0xoO)H!L literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtspy/PlayerRecord.class b/target/classes/com/bitnix/dirtspy/PlayerRecord.class new file mode 100644 index 0000000000000000000000000000000000000000..b231cab2852594007dc24a977c8f780e28f72423 GIT binary patch literal 3767 zcmai$-%lJ>6vxjj+l7TGKekX?XqVDbSr+M{wA2c1Z9#D93beqYv|5MVDIFMQ$n31x zXySt=K4{{DCO&B5gFb4aiNwSQ|A78c#_zc^yF1?8(QI;8ypmaNR>eNNL zM#B~Nps-{4j`d-oYI%O`@L6HaHlCT@1GD0KRgIGMx&_f&5);>b%Pwqg78hoDqHdjf zs8^%(6U(dl>!#^wq%HGEMyFFe(qS9H04oP{I!!OYe9yIXd1vr66bOvf)CYLp3<7yDZGJ+~uQ4Ee5a*c+Zv**B{keIK6d zh`&>31Me^aDJCO0mx91O1ruKvbw^zdL%GyT9| zAOw$&GrEoy7F&sh>|KTuZV_cNx6GzpFfE0dhA-H(zIYlwjxgacO);1zqDLF!B6WTQLvt<*}gf@*-ZP~OmS=emEXRStAEd8Gb9ae2MOlKU)b`ZLT zCYjsj^XOc6>t4lNv^Zh=qIKyCS0ft1El8jY_ELt+8F~r65<;Rd35hZ!Br1=PC@n&w zZU~8DAtWk9q~#}$ zsXd=Qrjy+C<=c*_Kc6{sOoO7$igrY_qxr-!jfrkvw3F;I#mzJ~GwgihH?i(rw2Rb- z_6#NIETw6Xx@iby_Z&3mDU07I4O1R$Q;5_xx`--r34EjQyG-{`6W3^rHYrE*nB{ap zObhe|zKF^qqNp{nUJCbBfYdN{BL6$J|B6n67$YT=6Ji=YFbJp7=uIj@!$FBU-;|vT zO6O%z2F|yz6rXg8qj|Fx0e=06rV;zAkzJ&kyGV(AVCoXR)dWeP7wqEXFX%*faa{?r zLaR-XHXJU44C6#)kXeAt#ei@^GRT?|q)!I9tpvHR2I-SQ<^i%01Hw0gK_1XLTKQ=D zf0iP$qy*VO;>brcgv^#f?g69}1HxC5LCVS;8I*HmRSB}C&XGYGWE~(IF(6zM802jw zNLB{fQi8mr2Fc1G4*~Kh284?egKR57Mr4r3N|1+YkP#WA0+4D92vo0T$UN+J!P_v$;s*}K_07FGu7^31X-t zl9xd~0?5ZPAp9^f$c_?ZQU>`%2~tsmOv)gi0_3w85PlgMq^c}vQ*uH3TnS>T3)++n z@&!P?i~-@NnL(Z?L8fJpuaqFWYLICer4~`d`~8*xCiVj~CSKwb8X|`GrEFlOv}D^7`79GJi4Q*d;71v! zrN$VW_%b_Z_M9`>nf>^DeFJcc9UUp8HDnA-Aj`1ps*c$TdOhizp4AoYz!u6piwZY^ zb$n=R=(^4@Gq<$HsX+2BKQ%e@{c2X@er z4#Q3@fp-x@39`yB2+*_%{P2*^{)6J7V8Z+P?orq;nMQo^us$o(#kYW0=?;| A?EnA( literal 0 HcmV?d00001 diff --git a/target/classes/config.yml b/target/classes/config.yml new file mode 100644 index 0000000..90b36cc --- /dev/null +++ b/target/classes/config.yml @@ -0,0 +1,35 @@ +settings: + save-on-disable: true + save-interval-seconds: 300 + track-ip-address: true + track-locale: true + track-client-view-distance: true + track-client-brand: true + log-joins-to-console: true + +messages: + prefix: "&8[&6DirtSpy&8] " + no-permission: "&cYou do not have permission." + player-only: "&cOnly players can use this command." + usage: "&eUsage: /dirtspy " + reloaded: "&aDirtSpy config reloaded." + player-not-found: "&cPlayer not found." + header: "&6DirtSpy report for &e%player%" + line-name: "&7Name: &f%value%" + line-uuid: "&7UUID: &f%value%" + line-first-seen: "&7First Seen: &f%value%" + line-last-seen: "&7Last Seen: &f%value%" + line-joins: "&7Join Count: &f%value%" + line-current-ip: "&7Current IP: &f%value%" + line-last-ip: "&7Last IP: &f%value%" + line-locale: "&7Locale: &f%value%" + line-brand: "&7Client Brand: &f%value%" + line-view-distance: "&7Client View Distance: &f%value%" + line-last-world: "&7Last World: &f%value%" + line-last-gamemode: "&7Last Gamemode: &f%value%" + line-last-join: "&7Last Join: &f%value%" + line-last-quit: "&7Last Quit: &f%value%" + line-total-playtime: "&7Tracked Playtime: &f%value% sec" + line-alt-count: "&7Accounts on Last IP: &f%value%" + line-alts: "&7Alt Names: &f%value%" + line-online: "&7Online: &f%value%" diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml new file mode 100644 index 0000000..47aefa6 --- /dev/null +++ b/target/classes/plugin.yml @@ -0,0 +1,25 @@ +name: DirtSpy +version: 1.0-SNAPSHOT +main: com.bitnix.dirtspy.DirtSpyPlugin +api-version: '1.21' +author: bitnix +description: Collects realistic client, connection, and behavior stats for admin review. + +commands: + dirtspy: + description: View DirtSpy info and reload the plugin + usage: /dirtspy + permission: dirtspy.use + +permissions: + dirtspy.use: + description: Allows use of DirtSpy commands + default: op + + dirtspy.reload: + description: Allows reloading DirtSpy config + default: op + + dirtspy.view: + description: Allows viewing collected DirtSpy data + default: op diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..65ed439 --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Sat Jun 13 16:22:51 EDT 2026 +artifactId=DirtSpy +groupId=com.bitnix +version=1.0-SNAPSHOT diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..e38c818 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,8 @@ +com/bitnix/dirtspy/DirtSpyCommand.class +com/bitnix/dirtspy/ClientBrandListener$VarIntResult.class +com/bitnix/dirtspy/PlayerRecord.class +com/bitnix/dirtspy/DirtSpyPlugin.class +com/bitnix/dirtspy/PlayerListener.class +com/bitnix/dirtspy/ClientBrandListener.class +com/bitnix/dirtspy/SaveTask.class +com/bitnix/dirtspy/PlayerDataManager.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..b4bb862 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,7 @@ +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/ClientBrandListener.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/DirtSpyCommand.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/DirtSpyPlugin.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/PlayerDataManager.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/PlayerListener.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/PlayerRecord.java +/home/bitnix/Desktop/DirtSpy/src/main/java/com/bitnix/dirtspy/SaveTask.java