diff --git a/lang/en.yml b/lang/en.yml index 8f123250..f0d823d3 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -208,4 +208,10 @@ VALID_DONATION_KEY: "Valid donation key." VERSION_INCOMPATIBLE: "{0} {1} is not supported." VERSION_NOTICE: "Version {0} is now available." VERSION_REQUIRED: "{0} {1} or higher is required." -WORLD_NOT_FOUND: "World \"{0}\" not found." \ No newline at end of file +WORLD_NOT_FOUND: "World \"{0}\" not found." + +# Export Feature +export-generating: Generating export file... +export-success: Successfully exported {0} {row|rows} to {1}. +export-failure: Failed to write export file. +export-error: An error occurred during export generation. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 12231501..ab3313a1 100755 --- a/pom.xml +++ b/pom.xml @@ -248,6 +248,12 @@ 3.45.1.0 test + + com.google.code.gson + gson + 2.10.1 + provided + org.slf4j slf4j-simple diff --git a/src/main/java/net/coreprotect/command/ApplyCommand.java b/src/main/java/net/coreprotect/command/ApplyCommand.java index eb886cce..1c3774be 100755 --- a/src/main/java/net/coreprotect/command/ApplyCommand.java +++ b/src/main/java/net/coreprotect/command/ApplyCommand.java @@ -11,9 +11,10 @@ import net.coreprotect.language.Selector; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; +import net.coreprotect.CoreProtect; public class ApplyCommand { - protected static void runCommand(CommandSender user, Command command, boolean permission, String[] args) { + protected static void runCommand(CoreProtect plugin, CommandSender user, Command command, boolean permission, String[] args) { try { if (ConfigHandler.lastRollback.get(user.getName()) != null) { List list = ConfigHandler.lastRollback.get(user.getName()); @@ -33,7 +34,7 @@ protected static void runCommand(CommandSender user, Command command, boolean pe } else { ConfigHandler.lastRollback.remove(user.getName()); - RollbackRestoreCommand.runCommand(user, command, permission, args, location, startTime, endTime); + RollbackRestoreCommand.runCommand(plugin, user, command, permission, args, location, startTime, endTime); } } else { diff --git a/src/main/java/net/coreprotect/command/CancelCommand.java b/src/main/java/net/coreprotect/command/CancelCommand.java index 4fe80e69..55473a61 100755 --- a/src/main/java/net/coreprotect/command/CancelCommand.java +++ b/src/main/java/net/coreprotect/command/CancelCommand.java @@ -11,9 +11,10 @@ import net.coreprotect.language.Selector; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; +import net.coreprotect.CoreProtect; public class CancelCommand { - protected static void runCommand(CommandSender user, Command command, boolean permission, String[] args) { + protected static void runCommand(CoreProtect plugin, CommandSender user, Command command, boolean permission, String[] args) { try { if (ConfigHandler.lastRollback.get(user.getName()) != null) { List list = ConfigHandler.lastRollback.get(user.getName()); @@ -33,7 +34,7 @@ protected static void runCommand(CommandSender user, Command command, boolean pe } else { ConfigHandler.lastRollback.remove(user.getName()); - RollbackRestoreCommand.runCommand(user, command, permission, args, location, startTime, endTime); + RollbackRestoreCommand.runCommand(plugin, user, command, permission, args, location, startTime, endTime); } } else { diff --git a/src/main/java/net/coreprotect/command/CommandHandler.java b/src/main/java/net/coreprotect/command/CommandHandler.java index a314e9d8..094d4a84 100755 --- a/src/main/java/net/coreprotect/command/CommandHandler.java +++ b/src/main/java/net/coreprotect/command/CommandHandler.java @@ -14,14 +14,20 @@ import net.coreprotect.utility.Color; import net.coreprotect.utility.Extensions; import net.coreprotect.utility.VersionUtils; +import net.coreprotect.CoreProtect; public class CommandHandler implements CommandExecutor { private static CommandHandler instance; private static ConcurrentHashMap versionAlert = new ConcurrentHashMap<>(); + private final CoreProtect plugin; - public static CommandHandler getInstance() { + public CommandHandler(CoreProtect plugin) { + this.plugin = plugin; + } + + public static CommandHandler getInstance(CoreProtect plugin) { if (instance == null) { - instance = new CommandHandler(); + instance = new CommandHandler(plugin); } return instance; } @@ -54,7 +60,7 @@ else if (user.hasPermission("coreprotect.help") && corecommand.equals("help")) { else if (user.hasPermission("coreprotect.purge") && corecommand.equals("purge")) { permission = true; } - else if (user.hasPermission("coreprotect.lookup") && (corecommand.equals("l") || corecommand.equals("lookup") || corecommand.equals("page") || corecommand.equals("near"))) { + else if (user.hasPermission("coreprotect.lookup") && (corecommand.equals("l") || corecommand.equals("lookup") || corecommand.equals("page") || corecommand.equals("near") || corecommand.equals("export"))) { permission = true; } else if (user.hasPermission("coreprotect.lookup.near") && corecommand.equals("near")) { @@ -78,16 +84,16 @@ else if (user.hasPermission("coreprotect.networking") && corecommand.equals("net } if (corecommand.equals("rollback") || corecommand.equals("restore") || corecommand.equals("rb") || corecommand.equals("rs") || corecommand.equals("ro") || corecommand.equals("re")) { - RollbackRestoreCommand.runCommand(user, command, permission, argumentArray, null, 0, 0); + RollbackRestoreCommand.runCommand(plugin, user, command, permission, argumentArray, null, 0, 0); } else if (corecommand.equals("apply")) { - ApplyCommand.runCommand(user, command, permission, argumentArray); + ApplyCommand.runCommand(plugin, user, command, permission, argumentArray); } else if (corecommand.equals("cancel")) { - CancelCommand.runCommand(user, command, permission, argumentArray); + CancelCommand.runCommand(plugin, user, command, permission, argumentArray); } else if (corecommand.equals("undo")) { - UndoCommand.runCommand(user, command, permission, argumentArray); + UndoCommand.runCommand(plugin, user, command, permission, argumentArray); } else if (corecommand.equals("help")) { HelpCommand.runCommand(user, permission, argumentArray); @@ -99,10 +105,13 @@ else if (corecommand.equals("inspect") || corecommand.equals("i")) { InspectCommand.runCommand(user, permission, argumentArray); } else if (corecommand.equals("lookup") || corecommand.equals("l") || corecommand.equals("page")) { - LookupCommand.runCommand(user, command, permission, argumentArray); + LookupCommand.runCommand(plugin, user, command, permission, argumentArray, false); } else if (corecommand.equals("near")) { - LookupCommand.runCommand(user, command, permission, new String[] { "near", "r:5x5" }); + LookupCommand.runCommand(plugin, user, command, permission, new String[] { "near", "r:5x5" }, false); + } + else if (corecommand.equals("export")) { + LookupCommand.runCommand(plugin, user, command, permission, argumentArray, true); } else if (corecommand.equals("teleport") || corecommand.equals("tp")) { TeleportCommand.runCommand(user, permission, argumentArray); diff --git a/src/main/java/net/coreprotect/command/LookupCommand.java b/src/main/java/net/coreprotect/command/LookupCommand.java index 9d454bb8..a4638575 100755 --- a/src/main/java/net/coreprotect/command/LookupCommand.java +++ b/src/main/java/net/coreprotect/command/LookupCommand.java @@ -15,6 +15,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import net.coreprotect.CoreProtect; import net.coreprotect.command.lookup.BlockLookupThread; import net.coreprotect.command.lookup.ChestTransactionLookupThread; import net.coreprotect.command.lookup.StandardLookupThread; @@ -28,7 +29,7 @@ import net.coreprotect.utility.WorldUtils; public class LookupCommand { - public static void runCommand(CommandSender player, Command command, boolean permission, String[] args) { + public static void runCommand(CoreProtect plugin, CommandSender player, Command command, boolean permission, String[] args, boolean exportMode) { int resultc = args.length; args = CommandParser.parsePage(args); Location lo = CommandParser.parseLocation(player, args); @@ -332,7 +333,7 @@ else if (resultc > 1) { double dz = 0.5 * (z + z2); final Location location = new Location(Bukkit.getServer().getWorld(world), dx, dy, dz); - Runnable runnable = new ChestTransactionLookupThread(player, command, location, p, re); + Runnable runnable = new ChestTransactionLookupThread(plugin, player, command, location, p, re, exportMode); Thread thread = new Thread(runnable); thread.start(); } @@ -396,7 +397,7 @@ else if (type == 2 || type == 3 || type == 7 || type == 8) { final Block block = Bukkit.getServer().getWorld(world).getBlockAt(x, y, z); final BlockState blockState = block.getState(); - Runnable runnable = new BlockLookupThread(player, command, block, blockState, page, re, type); + Runnable runnable = new BlockLookupThread(plugin, player, command, block, blockState, page, re, type, exportMode); Thread thread = new Thread(runnable); thread.start(); } @@ -595,7 +596,7 @@ else if (lookupType == 5) { } } - Runnable runnable = new StandardLookupThread(player, command, rollbackusers, argBlocks, argExclude, argExcludeUsers, argAction, argRadius, lo, x, y, z, wid, argWid, timeStart, timeEnd, argNoisy, argExcluded, argRestricted, pa, re, type, ts, count); + Runnable runnable = new StandardLookupThread(plugin, player, command, rollbackusers, argBlocks, argExclude, argExcludeUsers, argAction, argRadius, lo, x, y, z, wid, argWid, timeStart, timeEnd, argNoisy, argExcluded, argRestricted, pa, re, type, ts, count, exportMode); Thread thread = new Thread(runnable); thread.start(); } diff --git a/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java b/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java index b34f134b..e2d24a9d 100755 --- a/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java +++ b/src/main/java/net/coreprotect/command/RollbackRestoreCommand.java @@ -30,9 +30,10 @@ import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; import net.coreprotect.utility.WorldUtils; +import net.coreprotect.CoreProtect; public class RollbackRestoreCommand { - public static void runCommand(CommandSender player, Command command, boolean permission, String[] args, Location argLocation, long forceStart, long forceEnd) { + public static void runCommand(CoreProtect plugin, CommandSender player, Command command, boolean permission, String[] args, Location argLocation, long forceStart, long forceEnd) { Location lo = (argLocation != null ? argLocation : CommandParser.parseLocation(player, args)); List argUuids = new ArrayList<>(); List argUsers = CommandParser.parseUsers(args); @@ -99,7 +100,7 @@ else if (!argAction.contains(3)) { } if (count) { - LookupCommand.runCommand(player, command, permission, args); + LookupCommand.runCommand(plugin, player, command, permission, args, false); return; } if (ConfigHandler.converterRunning) { diff --git a/src/main/java/net/coreprotect/command/TabHandler.java b/src/main/java/net/coreprotect/command/TabHandler.java index 7f8acd99..df714761 100755 --- a/src/main/java/net/coreprotect/command/TabHandler.java +++ b/src/main/java/net/coreprotect/command/TabHandler.java @@ -86,6 +86,7 @@ private List getFirstLevelCompletions(CommandSender sender, String argum addCompletionIfPermitted(sender, "coreprotect.rollback", "rollback", completions); addCompletionIfPermitted(sender, "coreprotect.restore", "restore", completions); addCompletionIfPermitted(sender, "coreprotect.lookup", "lookup", completions); + addCompletionIfPermitted(sender, "coreprotect.lookup", "export", completions); addCompletionIfPermitted(sender, "coreprotect.purge", "purge", completions); addCompletionIfPermitted(sender, "coreprotect.reload", "reload", completions); addCompletionIfPermitted(sender, "coreprotect.status", "status", completions); @@ -118,7 +119,9 @@ private boolean hasPagePermission(CommandSender sender) { } private boolean hasLookupCommand(String cmd, CommandSender sender) { - return (sender.hasPermission("coreprotect.lookup") && (cmd.equals("l") || cmd.equals("lookup"))) || (sender.hasPermission("coreprotect.rollback") && (cmd.equals("rollback") || cmd.equals("rb") || cmd.equals("ro"))) || (sender.hasPermission("coreprotect.restore") && (cmd.equals("restore") || cmd.equals("rs") || cmd.equals("re"))); + return (sender.hasPermission("coreprotect.lookup") && (cmd.equals("l") || cmd.equals("lookup") || cmd.equals("export"))) + || (sender.hasPermission("coreprotect.rollback") && (cmd.equals("rollback") || cmd.equals("rb") || cmd.equals("ro"))) + || (sender.hasPermission("coreprotect.restore") && (cmd.equals("restore") || cmd.equals("rs") || cmd.equals("re"))); } private boolean isActionParam(String lastArg, String currentArg) { diff --git a/src/main/java/net/coreprotect/command/UndoCommand.java b/src/main/java/net/coreprotect/command/UndoCommand.java index 4784125c..047a1d2a 100755 --- a/src/main/java/net/coreprotect/command/UndoCommand.java +++ b/src/main/java/net/coreprotect/command/UndoCommand.java @@ -11,9 +11,10 @@ import net.coreprotect.language.Selector; import net.coreprotect.utility.Chat; import net.coreprotect.utility.Color; +import net.coreprotect.CoreProtect; public class UndoCommand { - protected static void runCommand(CommandSender user, Command command, boolean permission, String[] args) { + protected static void runCommand(CoreProtect plugin, CommandSender user, Command command, boolean permission, String[] args) { try { if (ConfigHandler.lastRollback.get(user.getName()) != null) { List list = ConfigHandler.lastRollback.get(user.getName()); @@ -23,7 +24,7 @@ protected static void runCommand(CommandSender user, Command command, boolean pe Location location = (Location) list.get(3); for (String arg : args) { if (arg.equals("#preview")) { - CancelCommand.runCommand(user, command, permission, args); + CancelCommand.runCommand(plugin, user, command, permission, args); return; } } @@ -39,7 +40,7 @@ else if (args[0].equals("restore") || args[0].equals("rs") || args[0].equals("re } if (valid) { ConfigHandler.lastRollback.remove(user.getName()); - RollbackRestoreCommand.runCommand(user, command, permission, args, location, startTime, endTime); + RollbackRestoreCommand.runCommand(plugin, user, command, permission, args, location, startTime, endTime); } } else { diff --git a/src/main/java/net/coreprotect/command/lookup/BlockLookupThread.java b/src/main/java/net/coreprotect/command/lookup/BlockLookupThread.java index fa7d92de..b4c1bf96 100644 --- a/src/main/java/net/coreprotect/command/lookup/BlockLookupThread.java +++ b/src/main/java/net/coreprotect/command/lookup/BlockLookupThread.java @@ -1,13 +1,27 @@ package net.coreprotect.command.lookup; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.sql.Connection; import java.sql.Statement; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.ChatColor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.coreprotect.CoreProtect; import net.coreprotect.config.ConfigHandler; import net.coreprotect.database.Database; import net.coreprotect.database.lookup.BlockLookup; @@ -18,6 +32,9 @@ import net.coreprotect.utility.Color; public class BlockLookupThread implements Runnable { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private final CoreProtect plugin; private final CommandSender player; private final Command command; private final Block block; @@ -25,8 +42,10 @@ public class BlockLookupThread implements Runnable { private final int page; private final int limit; private final int type; + private final boolean exportMode; - public BlockLookupThread(CommandSender player, Command command, Block block, BlockState blockState, int page, int limit, int type) { + public BlockLookupThread(CoreProtect plugin, CommandSender player, Command command, Block block, BlockState blockState, int page, int limit, int type, boolean exportMode) { + this.plugin = plugin; this.player = player; this.command = command; this.block = block; @@ -34,6 +53,7 @@ public BlockLookupThread(CommandSender player, Command command, Block block, Blo this.page = page; this.limit = limit; this.type = type; + this.exportMode = exportMode; } @Override @@ -43,36 +63,46 @@ public void run() { if (connection != null) { Statement statement = connection.createStatement(); if (type == 8) { - java.util.List signData = SignMessageLookup.performLookup(command.getName(), statement, blockState.getLocation(), player, page, limit); - for (String signMessage : signData) { - String bypass = null; - - if (signMessage.contains("\n")) { - String[] split = signMessage.split("\n"); - signMessage = split[0]; - bypass = split[1]; - } + if (exportMode) { + exportSignMessages(statement); + } + else { + java.util.List signData = SignMessageLookup.performLookup(command.getName(), statement, blockState.getLocation(), player, page, limit); + for (String signMessage : signData) { + String bypass = null; + + if (signMessage.contains("\n")) { + String[] split = signMessage.split("\n"); + signMessage = split[0]; + bypass = split[1]; + } - if (signMessage.length() > 0) { - Chat.sendComponent(player, signMessage, bypass); + if (signMessage.length() > 0) { + Chat.sendComponent(player, signMessage, bypass); + } } } } else { - String blockdata = null; - if (type == 7) { - blockdata = InteractionLookup.performLookup(command.getName(), statement, block, player, 0, page, limit); + if (exportMode) { + exportBlockInteractions(statement); } else { - blockdata = BlockLookup.performLookup(command.getName(), statement, blockState, player, 0, page, limit); - } - if (blockdata.contains("\n")) { - for (String b : blockdata.split("\n")) { - Chat.sendComponent(player, b); + String blockdata = null; + if (type == 7) { + blockdata = InteractionLookup.performLookup(command.getName(), statement, block, player, 0, page, limit); + } + else { + blockdata = BlockLookup.performLookup(command.getName(), statement, blockState, player, 0, page, limit); + } + if (blockdata.contains("\n")) { + for (String b : blockdata.split("\n")) { + Chat.sendComponent(player, b); + } + } + else if (blockdata.length() > 0) { + Chat.sendComponent(player, blockdata); } - } - else if (blockdata.length() > 0) { - Chat.sendComponent(player, blockdata); } } statement.close(); @@ -87,4 +117,129 @@ else if (blockdata.length() > 0) { ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { false, System.currentTimeMillis() }); } + + private void exportSignMessages(Statement statement) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.ITALIC + Phrase.build(Phrase.EXPORT_GENERATING)); + List allSignData = new ArrayList<>(); + int currentPage = 1; + boolean dataFound = false; + try { + while (true) { + List pageData = SignMessageLookup.performLookup(command.getName(), statement, blockState.getLocation(), player, currentPage, limit); + if (pageData == null || pageData.isEmpty() || (pageData.size() == 1 && pageData.get(0).isEmpty())) { + if (currentPage == 1 && !dataFound) { + pageData = SignMessageLookup.performLookup(command.getName(), statement, blockState.getLocation(), player, currentPage, limit); + if (pageData == null || pageData.isEmpty() || (pageData.size() == 1 && pageData.get(0).isEmpty())) { + break; + } + } else { + break; + } + } + dataFound = true; + + for (String line : pageData) { + if (!line.startsWith("---") && !line.contains("Page ")) { + allSignData.add(ChatColor.stripColor(line.replace("\n", " | "))); + } + } + currentPage++; + + if (currentPage > 1000) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + "Export limit reached (1000 pages)."); + break; + } + } + + if (!dataFound) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); + } + else { + saveExport("sign", String.join("\n", allSignData)); + } + } + catch (Exception e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_ERROR)); + e.printStackTrace(); + } + } + + private void exportBlockInteractions(Statement statement) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.ITALIC + Phrase.build(Phrase.EXPORT_GENERATING)); + List allBlockData = new ArrayList<>(); + int currentPage = 1; + boolean dataFound = false; + try { + while (true) { + String blockdataPage = null; + if (type == 7) { + blockdataPage = InteractionLookup.performLookup(command.getName(), statement, block, player, 0, currentPage, limit); + } + else { + blockdataPage = BlockLookup.performLookup(command.getName(), statement, blockState, player, 0, currentPage, limit); + } + + if (blockdataPage == null || blockdataPage.isEmpty() || blockdataPage.contains(Phrase.build(Phrase.NO_RESULTS)) || blockdataPage.contains(Phrase.build(Phrase.NO_RESULTS_PAGE))) { + if (currentPage == 1 && !dataFound) { + if (blockdataPage != null && !blockdataPage.isEmpty() && !blockdataPage.contains(Phrase.build(Phrase.NO_RESULTS))) { + } else { + break; + } + } else { + break; + } + } + dataFound = true; + + String[] lines = blockdataPage.split("\n"); + for (String line : lines) { + if (!line.startsWith("---") && !line.contains("Page ")) { + allBlockData.add(ChatColor.stripColor(line)); + } + } + currentPage++; + + if (currentPage > 1000) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + "Export limit reached (1000 pages)."); + break; + } + } + + if (!dataFound) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); + } + else { + saveExport(type == 7 ? "interaction" : "block", String.join("\n", allBlockData)); + } + } + catch (Exception e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_ERROR)); + e.printStackTrace(); + } + } + + private void saveExport(String exportType, String rawData) { + File exportDir = new File(plugin.getDataFolder(), "exports"); + if (!exportDir.exists()) { + exportDir.mkdirs(); + } + + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String filename = "export_" + timestamp + "_" + player.getName().replaceAll("[^a-zA-Z0-9_]", "") + "_" + exportType + ".json"; + File exportFile = new File(exportDir, filename); + + Map exportData = new HashMap<>(); + exportData.put("type", exportType); + exportData.put("raw_data", rawData); + + try (FileWriter writer = new FileWriter(exportFile)) { + GSON.toJson(exportData, writer); + int lineCount = rawData.isEmpty() ? 0 : rawData.split("\n").length; + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.EXPORT_SUCCESS, String.valueOf(lineCount), "exports/" + filename)); + } + catch (IOException e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_FAILURE)); + e.printStackTrace(); + } + } } diff --git a/src/main/java/net/coreprotect/command/lookup/ChestTransactionLookupThread.java b/src/main/java/net/coreprotect/command/lookup/ChestTransactionLookupThread.java index 045f9437..e7fa6be8 100644 --- a/src/main/java/net/coreprotect/command/lookup/ChestTransactionLookupThread.java +++ b/src/main/java/net/coreprotect/command/lookup/ChestTransactionLookupThread.java @@ -1,13 +1,26 @@ package net.coreprotect.command.lookup; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.sql.Connection; import java.sql.Statement; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.bukkit.Location; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.ChatColor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.coreprotect.CoreProtect; import net.coreprotect.config.ConfigHandler; import net.coreprotect.database.Database; import net.coreprotect.database.lookup.ChestTransactionLookup; @@ -16,18 +29,24 @@ import net.coreprotect.utility.Color; public class ChestTransactionLookupThread implements Runnable { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private final CoreProtect plugin; private final CommandSender player; private final Command command; private final Location location; private final int page; private final int limit; + private final boolean exportMode; - public ChestTransactionLookupThread(CommandSender player, Command command, Location location, int page, int limit) { + public ChestTransactionLookupThread(CoreProtect plugin, CommandSender player, Command command, Location location, int page, int limit, boolean exportMode) { + this.plugin = plugin; this.player = player; this.command = command; this.location = location; this.page = page; this.limit = limit; + this.exportMode = exportMode; } @Override @@ -36,10 +55,17 @@ public void run() { ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { true, System.currentTimeMillis() }); if (connection != null) { Statement statement = connection.createStatement(); - List blockData = ChestTransactionLookup.performLookup(command.getName(), statement, location, player, page, limit, false); - for (String data : blockData) { - Chat.sendComponent(player, data); + + if (exportMode) { + exportContainerTransactions(statement); + } + else { + List blockData = ChestTransactionLookup.performLookup(command.getName(), statement, location, player, page, limit, false); + for (String data : blockData) { + Chat.sendComponent(player, data); + } } + statement.close(); } else { @@ -52,4 +78,85 @@ public void run() { ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { false, System.currentTimeMillis() }); } + + private void exportContainerTransactions(Statement statement) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.ITALIC + Phrase.build(Phrase.EXPORT_GENERATING)); + List allContainerData = new ArrayList<>(); + int currentPage = 1; + boolean dataFound = false; + try { + while (true) { + // Note: ChestTransactionLookup already handles pagination internally somewhat differently. + // We request pages until it returns an empty list or a list signifying the end. + List pageData = ChestTransactionLookup.performLookup(command.getName(), statement, location, player, currentPage, limit, true); // Set finalPage=true for export loop + + if (pageData == null || pageData.isEmpty() || (pageData.size() == 1 && pageData.get(0).contains(Phrase.build(Phrase.NO_RESULTS_PAGE)))) { + if (currentPage == 1 && !dataFound) { // Check if first page might just have header/footer + if (pageData != null && !pageData.isEmpty() && !pageData.get(0).contains(Phrase.build(Phrase.NO_RESULTS))) { + // Contains header/footer + } else { + break; // Truly empty + } + } else { + break; // No more data + } + } + dataFound = true; + + // Filter out header/footer/page navigation + for (String line : pageData) { + if (!line.startsWith("---") && !line.contains("Page ")) { // Basic filtering + allContainerData.add(ChatColor.stripColor(line)); + } + } + currentPage++; + + // Safety break + if (currentPage > 1000) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + "Export limit reached (1000 pages)."); + break; + } + } + + if (!dataFound) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); + } + else { + saveExport("container", String.join("\n", allContainerData)); + } + } + catch (Exception e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_ERROR)); + e.printStackTrace(); + } + } + + private void saveExport(String exportType, String rawData) { + // Ensure exports directory exists + File exportDir = new File(plugin.getDataFolder(), "exports"); + if (!exportDir.exists()) { + exportDir.mkdirs(); + } + + // Generate filename + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String filename = "export_" + timestamp + "_" + player.getName().replaceAll("[^a-zA-Z0-9_]", "") + "_" + exportType + ".json"; + File exportFile = new File(exportDir, filename); + + // Create JSON structure + Map exportData = new HashMap<>(); + exportData.put("type", exportType); + exportData.put("raw_data", rawData); + + // Write JSON to file + try (FileWriter writer = new FileWriter(exportFile)) { + GSON.toJson(exportData, writer); + int lineCount = rawData.isEmpty() ? 0 : rawData.split("\n").length; + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.EXPORT_SUCCESS, String.valueOf(lineCount), "exports/" + filename)); + } + catch (IOException e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_FAILURE)); + e.printStackTrace(); + } + } } diff --git a/src/main/java/net/coreprotect/command/lookup/StandardLookupThread.java b/src/main/java/net/coreprotect/command/lookup/StandardLookupThread.java index f6540ff0..65561155 100644 --- a/src/main/java/net/coreprotect/command/lookup/StandardLookupThread.java +++ b/src/main/java/net/coreprotect/command/lookup/StandardLookupThread.java @@ -1,10 +1,16 @@ package net.coreprotect.command.lookup; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.Statement; import java.text.NumberFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -16,7 +22,10 @@ import org.bukkit.command.CommandSender; import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.coreprotect.CoreProtect; import net.coreprotect.config.ConfigHandler; import net.coreprotect.database.Database; import net.coreprotect.database.Lookup; @@ -37,6 +46,10 @@ import net.coreprotect.utility.WorldUtils; public class StandardLookupThread implements Runnable { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final int EXPORT_BATCH_SIZE = 500; // Batch size for fetching data during export + + private final CoreProtect plugin; private final CommandSender player; private final Command command; private final List rollbackUsers; @@ -61,8 +74,10 @@ public class StandardLookupThread implements Runnable { private final int typeLookup; private final String rtime; private final boolean count; + private final boolean exportMode; // Added for export functionality - public StandardLookupThread(CommandSender player, Command command, List rollbackUsers, List blockList, Map excludedBlocks, List excludedUsers, List actions, Integer[] radius, Location location, int x, int y, int z, int worldId, int argWorldId, long timeStart, long timeEnd, int noisy, int excluded, int restricted, int page, int displayResults, int typeLookup, String rtime, boolean count) { + public StandardLookupThread(CoreProtect plugin, CommandSender player, Command command, List rollbackUsers, List blockList, Map excludedBlocks, List excludedUsers, List actions, Integer[] radius, Location location, int x, int y, int z, int worldId, int argWorldId, long timeStart, long timeEnd, int noisy, int excluded, int restricted, int page, int displayResults, int typeLookup, String rtime, boolean count, boolean exportMode) { + this.plugin = plugin; this.player = player; this.command = command; this.rollbackUsers = rollbackUsers; @@ -87,6 +102,7 @@ public StandardLookupThread(CommandSender player, Command command, List this.typeLookup = typeLookup; this.rtime = rtime; this.count = count; + this.exportMode = exportMode; } @Override @@ -188,268 +204,327 @@ else if (finalLocation != null) { rowData[3] = rows; ConfigHandler.lookupRows.put(player.getName(), rowData); } - if (count) { - String row_format = NumberFormat.getInstance().format(rows); - Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.LOOKUP_ROWS_FOUND, row_format, (rows == 1 ? Selector.FIRST : Selector.SECOND))); - } - else if (pageStart < rows) { - List lookupList = Lookup.performPartialLookup(statement, player, uuidList, userList, blockList, excludedBlocks, excludedUsers, actions, finalLocation, radius, rowData, timeStart, timeEnd, (int) pageStart, displayResults, restrict_world, true); - - Chat.sendMessage(player, Color.WHITE + "----- " + Color.DARK_AQUA + Phrase.build(Phrase.LOOKUP_HEADER, "CoreProtect" + Color.WHITE + " | " + Color.DARK_AQUA) + Color.WHITE + " -----"); - if (actions.contains(6) || actions.contains(7)) { // Chat/command - for (String[] data : lookupList) { - String time = data[0]; - String dplayer = data[1]; - String message = data[2]; - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Color.DARK_AQUA + dplayer + ": " + Color.WHITE, message); - if (PluginChannelHandshakeListener.getInstance().isPluginChannelPlayer(player)) { - int wid = Integer.parseInt(data[3]); - int dataX = Integer.parseInt(data[4]); - int dataY = Integer.parseInt(data[5]); - int dataZ = Integer.parseInt(data[6]); - PluginChannelListener.getInstance().sendMessageData(player, Integer.parseInt(time), dplayer, message, false, dataX, dataY, dataZ, wid); - } - } - } - else if (actions.contains(8)) { // login/logouts - for (String[] data : lookupList) { - String time = data[0]; - String dplayer = data[1]; - int wid = Integer.parseInt(data[2]); - int dataX = Integer.parseInt(data[3]); - int dataY = Integer.parseInt(data[4]); - int dataZ = Integer.parseInt(data[5]); - int action = Integer.parseInt(data[6]); - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); - String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); - if (timeLength % 4 == 0) { - leftPadding = Strings.padStart("", timeLength / 4, ' '); - } - else { - leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); - } - String tag = (action != 0 ? Color.GREEN + "+" : Color.RED + "-"); - Chat.sendComponent(player, timeago + " " + tag + " " + Color.DARK_AQUA + Phrase.build(Phrase.LOOKUP_LOGIN, Color.DARK_AQUA + dplayer + Color.WHITE, (action != 0 ? Selector.FIRST : Selector.SECOND))); - Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + ""); - PluginChannelListener.getInstance().sendInfoData(player, Integer.parseInt(time), Phrase.LOOKUP_LOGIN, (action != 0 ? Selector.FIRST : Selector.SECOND), dplayer, -1, dataX, dataY, dataZ, wid); - } - } - else if (actions.contains(9)) { // username-changes - for (String[] data : lookupList) { - String time = data[0]; - String user = ConfigHandler.uuidCacheReversed.get(data[1]); - String username = data[2]; - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Phrase.build(Phrase.LOOKUP_USERNAME, Color.DARK_AQUA + user + Color.WHITE, Color.DARK_AQUA + username + Color.WHITE)); - PluginChannelListener.getInstance().sendUsernameData(player, Integer.parseInt(time), user, username); - } - } - else if (actions.contains(10)) { // sign messages - for (String[] data : lookupList) { - String time = data[0]; - String dplayer = data[1]; - int wid = Integer.parseInt(data[2]); - int dataX = Integer.parseInt(data[3]); - int dataY = Integer.parseInt(data[4]); - int dataZ = Integer.parseInt(data[5]); - String message = data[6]; - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); - String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); - if (timeLength % 4 == 0) { - leftPadding = Strings.padStart("", timeLength / 4, ' '); + if (exportMode) { + // --- Export Mode --- // + List> allResults = new ArrayList<>(); + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.ITALIC + Phrase.build(Phrase.EXPORT_GENERATING)); + + long currentPageStart = 0; + boolean dataFound = false; + try { + while (true) { + List lookupList = Lookup.performPartialLookup(statement, player, uuidList, userList, blockList, excludedBlocks, excludedUsers, actions, finalLocation, radius, rowData, timeStart, timeEnd, (int) currentPageStart, EXPORT_BATCH_SIZE, restrict_world, true); + if (lookupList == null || lookupList.isEmpty()) { + break; } - else { - leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); + dataFound = true; + + for (String[] data : lookupList) { + Map resultData = mapResultData(data, actions, connection); + if (resultData != null) { + allResults.add(resultData); + } } + currentPageStart += EXPORT_BATCH_SIZE; + } - Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Color.DARK_AQUA + dplayer + ": " + Color.WHITE, message); - Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + ""); - PluginChannelListener.getInstance().sendMessageData(player, Integer.parseInt(time), dplayer, message, true, dataX, dataY, dataZ, wid); + if (!dataFound) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); } - } - else if (actions.contains(4) && actions.contains(11)) { // inventory transactions - for (String[] data : lookupList) { - String time = data[0]; - String dplayer = data[1]; - int dtype = Integer.parseInt(data[5]); - int ddata = Integer.parseInt(data[6]); - int daction = Integer.parseInt(data[7]); - int amount = Integer.parseInt(data[10]); - int wid = Integer.parseInt(data[9]); - int dataX = Integer.parseInt(data[2]); - int dataY = Integer.parseInt(data[3]); - int dataZ = Integer.parseInt(data[4]); - String rbd = ((Integer.parseInt(data[8]) == 2 || Integer.parseInt(data[8]) == 3) ? Color.STRIKETHROUGH : ""); - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - Material blockType = ItemUtils.itemFilter(MaterialUtils.getType(dtype), (Integer.parseInt(data[13]) == 0)); - String dname = StringUtils.nameFilter(blockType.name().toLowerCase(Locale.ROOT), ddata); - byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1); - String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount); - - String selector = Selector.FIRST; - String tag = Color.WHITE + "-"; - if (daction == 2 || daction == 3) { // LOOKUP_ITEM - selector = (daction != 2 ? Selector.FIRST : Selector.SECOND); - tag = (daction != 2 ? Color.GREEN + "+" : Color.RED + "-"); - } - else if (daction == 4 || daction == 5) { // LOOKUP_STORAGE - selector = (daction == 4 ? Selector.FIRST : Selector.SECOND); - tag = (daction == 4 ? Color.GREEN + "+" : Color.RED + "-"); - } - else if (daction == 6 || daction == 7) { // LOOKUP_PROJECTILE - selector = Selector.SECOND; - tag = Color.RED + "-"; + else { + // Ensure exports directory exists + File exportDir = new File(plugin.getDataFolder(), "exports"); + if (!exportDir.exists()) { + exportDir.mkdirs(); } - else if (daction == ItemLogger.ITEM_BREAK || daction == ItemLogger.ITEM_DESTROY || daction == ItemLogger.ITEM_CREATE) { - selector = (daction == ItemLogger.ITEM_CREATE ? Selector.FIRST : Selector.SECOND); - tag = (daction == ItemLogger.ITEM_CREATE ? Color.GREEN + "+" : Color.RED + "-"); - } - else if (daction == ItemLogger.ITEM_SELL || daction == ItemLogger.ITEM_BUY) { // LOOKUP_TRADE - selector = (daction == ItemLogger.ITEM_BUY ? Selector.FIRST : Selector.SECOND); - tag = (daction == ItemLogger.ITEM_BUY ? Color.GREEN + "+" : Color.RED + "-"); + + // Generate filename + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String filename = "export_" + timestamp + "_" + player.getName().replaceAll("[^a-zA-Z0-9_]", "") + ".json"; + File exportFile = new File(exportDir, filename); + + // Write JSON to file + try (FileWriter writer = new FileWriter(exportFile)) { + GSON.toJson(allResults, writer); + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.EXPORT_SUCCESS, String.valueOf(allResults.size()), "exports/" + filename)); } - else { // LOOKUP_CONTAINER - selector = (daction == 0 ? Selector.FIRST : Selector.SECOND); - tag = (daction == 0 ? Color.GREEN + "+" : Color.RED + "-"); + catch (IOException e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_FAILURE)); + e.printStackTrace(); } - - Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(Phrase.LOOKUP_CONTAINER, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector)); - PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), Phrase.LOOKUP_CONTAINER, selector, dplayer, dname, amount, dataX, dataY, dataZ, wid, rbd, true, tag.contains("+")); } } - else { - for (String[] data : lookupList) { - int drb = Integer.parseInt(data[8]); - String rbd = ""; - if (drb == 1 || drb == 3) { - rbd = Color.STRIKETHROUGH; - } + catch (Exception e) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Color.RED + Phrase.build(Phrase.EXPORT_ERROR)); + e.printStackTrace(); + } + } + else { + // --- Original Chat Output Mode --- // + if (count) { + String row_format = NumberFormat.getInstance().format(rows); + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.LOOKUP_ROWS_FOUND, row_format, (rows == 1 ? Selector.FIRST : Selector.SECOND))); + } + else if (pageStart < rows) { + List lookupList = Lookup.performPartialLookup(statement, player, uuidList, userList, blockList, excludedBlocks, excludedUsers, actions, finalLocation, radius, rowData, timeStart, timeEnd, (int) pageStart, displayResults, restrict_world, true); - String time = data[0]; - String dplayer = data[1]; - int dataX = Integer.parseInt(data[2]); - int dataY = Integer.parseInt(data[3]); - int dataZ = Integer.parseInt(data[4]); - int dtype = Integer.parseInt(data[5]); - int ddata = Integer.parseInt(data[6]); - int daction = Integer.parseInt(data[7]); - int wid = Integer.parseInt(data[9]); - int amount = Integer.parseInt(data[10]); - String tag = Color.WHITE + "-"; - - String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); - int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); - String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); - if (timeLength % 4 == 0) { - leftPadding = Strings.padStart("", timeLength / 4, ' '); - } - else { - leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); + Chat.sendMessage(player, Color.WHITE + "----- " + Color.DARK_AQUA + Phrase.build(Phrase.LOOKUP_HEADER, "CoreProtect" + Color.WHITE + " | " + Color.DARK_AQUA) + Color.WHITE + " -----"); + if (actions.contains(6) || actions.contains(7)) { // Chat/command + for (String[] data : lookupList) { + String time = data[0]; + String dplayer = data[1]; + String message = data[2]; + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Color.DARK_AQUA + dplayer + ": " + Color.WHITE, message); + if (PluginChannelHandshakeListener.getInstance().isPluginChannelPlayer(player)) { + int wid = Integer.parseInt(data[3]); + int dataX = Integer.parseInt(data[4]); + int dataY = Integer.parseInt(data[5]); + int dataZ = Integer.parseInt(data[6]); + PluginChannelListener.getInstance().sendMessageData(player, Integer.parseInt(time), dplayer, message, false, dataX, dataY, dataZ, wid); + } } - - String dname = ""; - boolean isPlayer = false; - if (daction == 3 && !actions.contains(11) && amount == -1) { - if (dtype == 0) { - if (ConfigHandler.playerIdCacheReversed.get(ddata) == null) { - UserStatement.loadName(connection, ddata); - } - dname = ConfigHandler.playerIdCacheReversed.get(ddata); - isPlayer = true; + } + else if (actions.contains(8)) { // login/logouts + for (String[] data : lookupList) { + String time = data[0]; + String dplayer = data[1]; + int wid = Integer.parseInt(data[2]); + int dataX = Integer.parseInt(data[3]); + int dataY = Integer.parseInt(data[4]); + int dataZ = Integer.parseInt(data[5]); + int action = Integer.parseInt(data[6]); + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); + String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); + if (timeLength % 4 == 0) { + leftPadding = Strings.padStart("", timeLength / 4, ' '); } else { - dname = EntityUtils.getEntityType(dtype).name(); + leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); } + + String tag = (action != 0 ? Color.GREEN + "+" : Color.RED + "-"); + Chat.sendComponent(player, timeago + " " + tag + " " + Color.DARK_AQUA + Phrase.build(Phrase.LOOKUP_LOGIN, Color.DARK_AQUA + dplayer + Color.WHITE, (action != 0 ? Selector.FIRST : Selector.SECOND))); + Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + ""); + PluginChannelListener.getInstance().sendInfoData(player, Integer.parseInt(time), Phrase.LOOKUP_LOGIN, (action != 0 ? Selector.FIRST : Selector.SECOND), dplayer, -1, dataX, dataY, dataZ, wid); } - else { - dname = MaterialUtils.getType(dtype).name().toLowerCase(Locale.ROOT); - dname = StringUtils.nameFilter(dname, ddata); - } - if (dname.length() > 0 && !isPlayer) { - dname = "minecraft:" + dname.toLowerCase(Locale.ROOT) + ""; + } + else if (actions.contains(9)) { // username-changes + for (String[] data : lookupList) { + String time = data[0]; + String user = ConfigHandler.uuidCacheReversed.get(data[1]); + String username = data[2]; + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Phrase.build(Phrase.LOOKUP_USERNAME, Color.DARK_AQUA + user + Color.WHITE, Color.DARK_AQUA + username + Color.WHITE)); + PluginChannelListener.getInstance().sendUsernameData(player, Integer.parseInt(time), user, username); } + } + else if (actions.contains(10)) { // sign messages + for (String[] data : lookupList) { + String time = data[0]; + String dplayer = data[1]; + int wid = Integer.parseInt(data[2]); + int dataX = Integer.parseInt(data[3]); + int dataY = Integer.parseInt(data[4]); + int dataZ = Integer.parseInt(data[5]); + String message = data[6]; + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); + String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); + if (timeLength % 4 == 0) { + leftPadding = Strings.padStart("", timeLength / 4, ' '); + } + else { + leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); + } - // Hide "minecraft:" for now. - if (dname.contains("minecraft:")) { - String[] blockNameSplit = dname.split(":"); - dname = blockNameSplit[1]; + Chat.sendComponent(player, timeago + " " + Color.WHITE + "- " + Color.DARK_AQUA + dplayer + ": " + Color.WHITE, message); + Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + ""); + PluginChannelListener.getInstance().sendMessageData(player, Integer.parseInt(time), dplayer, message, true, dataX, dataY, dataZ, wid); } - - // Functions.sendMessage(player2, timeago+" " + ChatColors.WHITE + "- " + ChatColors.DARK_AQUA+rbd+""+dplayer+" " + ChatColors.WHITE+rbd+""+a+" " + ChatColors.DARK_AQUA+rbd+"#"+dtype+ChatColors.WHITE + ". " + ChatColors.GREY + "(x"+x+"/y"+y+"/z"+z+")"); - - Phrase phrase = Phrase.LOOKUP_BLOCK; - String selector = Selector.FIRST; - String action = "a:block"; - if (actions.contains(4) || actions.contains(5) || actions.contains(11) || amount > -1) { + } + else if (actions.contains(4) && actions.contains(11)) { // inventory transactions + for (String[] data : lookupList) { + String time = data[0]; + String dplayer = data[1]; + int dtype = Integer.parseInt(data[5]); + int ddata = Integer.parseInt(data[6]); + int daction = Integer.parseInt(data[7]); + int amount = Integer.parseInt(data[10]); + int wid = Integer.parseInt(data[9]); + int dataX = Integer.parseInt(data[2]); + int dataY = Integer.parseInt(data[3]); + int dataZ = Integer.parseInt(data[4]); + String rbd = ((Integer.parseInt(data[8]) == 2 || Integer.parseInt(data[8]) == 3) ? Color.STRIKETHROUGH : ""); + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + Material blockType = ItemUtils.itemFilter(MaterialUtils.getType(dtype), (Integer.parseInt(data[13]) == 0)); + String dname = StringUtils.nameFilter(blockType.name().toLowerCase(Locale.ROOT), ddata); byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1); String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount); - if (daction == 2 || daction == 3) { - phrase = Phrase.LOOKUP_ITEM; // {picked up|dropped} + String selector = Selector.FIRST; + String tag = Color.WHITE + "-"; + if (daction == 2 || daction == 3) { // LOOKUP_ITEM selector = (daction != 2 ? Selector.FIRST : Selector.SECOND); tag = (daction != 2 ? Color.GREEN + "+" : Color.RED + "-"); - action = "a:item"; } - else if (daction == 4 || daction == 5) { - phrase = Phrase.LOOKUP_STORAGE; // {deposited|withdrew} - selector = (daction != 4 ? Selector.FIRST : Selector.SECOND); - tag = (daction != 4 ? Color.RED + "-" : Color.GREEN + "+"); - action = "a:item"; + else if (daction == 4 || daction == 5) { // LOOKUP_STORAGE + selector = (daction == 4 ? Selector.FIRST : Selector.SECOND); + tag = (daction == 4 ? Color.GREEN + "+" : Color.RED + "-"); } - else if (daction == 6 || daction == 7) { - phrase = Phrase.LOOKUP_PROJECTILE; // {threw|shot} - selector = (daction != 7 ? Selector.FIRST : Selector.SECOND); + else if (daction == 6 || daction == 7) { // LOOKUP_PROJECTILE + selector = Selector.SECOND; tag = Color.RED + "-"; - action = "a:item"; } - else { - phrase = Phrase.LOOKUP_CONTAINER; // {added|removed} - selector = (daction != 0 ? Selector.FIRST : Selector.SECOND); - tag = (daction != 0 ? Color.GREEN + "+" : Color.RED + "-"); - action = "a:container"; + else if (daction == ItemLogger.ITEM_BREAK || daction == ItemLogger.ITEM_DESTROY || daction == ItemLogger.ITEM_CREATE) { + selector = (daction == ItemLogger.ITEM_CREATE ? Selector.FIRST : Selector.SECOND); + tag = (daction == ItemLogger.ITEM_CREATE ? Color.GREEN + "+" : Color.RED + "-"); + } + else if (daction == ItemLogger.ITEM_SELL || daction == ItemLogger.ITEM_BUY) { // LOOKUP_TRADE + selector = (daction == ItemLogger.ITEM_BUY ? Selector.FIRST : Selector.SECOND); + tag = (daction == ItemLogger.ITEM_BUY ? Color.GREEN + "+" : Color.RED + "-"); + } + else { // LOOKUP_CONTAINER + selector = (daction == 0 ? Selector.FIRST : Selector.SECOND); + tag = (daction == 0 ? Color.GREEN + "+" : Color.RED + "-"); } - Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector)); - PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), phrase, selector, dplayer, dname, (tag.contains("+") ? 1 : -1), dataX, dataY, dataZ, wid, rbd, action.contains("container"), tag.contains("+")); + Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(Phrase.LOOKUP_CONTAINER, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector)); + PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), Phrase.LOOKUP_CONTAINER, selector, dplayer, dname, amount, dataX, dataY, dataZ, wid, rbd, true, tag.contains("+")); } - else { - if (daction == 2 || daction == 3) { - phrase = Phrase.LOOKUP_INTERACTION; // {clicked|killed} - selector = (daction != 3 ? Selector.FIRST : Selector.SECOND); - tag = (daction != 3 ? Color.WHITE + "-" : Color.RED + "-"); - action = (daction == 2 ? "a:click" : "a:kill"); + } + else { + for (String[] data : lookupList) { + int drb = Integer.parseInt(data[8]); + String rbd = ""; + if (drb == 1 || drb == 3) { + rbd = Color.STRIKETHROUGH; + } + + String time = data[0]; + String dplayer = data[1]; + int dataX = Integer.parseInt(data[2]); + int dataY = Integer.parseInt(data[3]); + int dataZ = Integer.parseInt(data[4]); + int dtype = Integer.parseInt(data[5]); + int ddata = Integer.parseInt(data[6]); + int daction = Integer.parseInt(data[7]); + int wid = Integer.parseInt(data[9]); + int amount = Integer.parseInt(data[10]); + String tag = Color.WHITE + "-"; + + String timeago = ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, true); + int timeLength = 50 + (ChatUtils.getTimeSince(Integer.parseInt(time), unixtimestamp, false).replaceAll("[^0-9]", "").length() * 6); + String leftPadding = Color.BOLD + Strings.padStart("", 10, ' '); + if (timeLength % 4 == 0) { + leftPadding = Strings.padStart("", timeLength / 4, ' '); } else { - phrase = Phrase.LOOKUP_BLOCK; // {placed|broke} - selector = (daction != 0 ? Selector.FIRST : Selector.SECOND); - tag = (daction != 0 ? Color.GREEN + "+" : Color.RED + "-"); + leftPadding = leftPadding + Color.WHITE + Strings.padStart("", (timeLength - 50) / 4, ' '); } - Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, Color.DARK_AQUA + rbd + dname + Color.WHITE, selector)); - PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), phrase, selector, dplayer, dname, (tag.contains("+") ? 1 : -1), dataX, dataY, dataZ, wid, rbd, false, tag.contains("+")); - } + String dname = ""; + boolean isPlayer = false; + if (daction == 3 && !actions.contains(11) && amount == -1) { + if (dtype == 0) { + if (ConfigHandler.playerIdCacheReversed.get(ddata) == null) { + UserStatement.loadName(connection, ddata); + } + dname = ConfigHandler.playerIdCacheReversed.get(ddata); + isPlayer = true; + } + else { + dname = EntityUtils.getEntityType(dtype).name(); + } + } + else { + dname = MaterialUtils.getType(dtype).name().toLowerCase(Locale.ROOT); + dname = StringUtils.nameFilter(dname, ddata); + } + if (dname.length() > 0 && !isPlayer) { + dname = "minecraft:" + dname.toLowerCase(Locale.ROOT) + ""; + } + + // Hide "minecraft:" for now. + if (dname.contains("minecraft:")) { + String[] blockNameSplit = dname.split(":"); + dname = blockNameSplit[1]; + } + + // Functions.sendMessage(player2, timeago+" " + ChatColors.WHITE + "- " + ChatColors.DARK_AQUA+rbd+""+dplayer+" " + ChatColors.WHITE+rbd+""+a+" " + ChatColors.DARK_AQUA+rbd+"#"+dtype+ChatColors.WHITE + ". " + ChatColors.GREY + "(x"+x+"/y"+y+"/z"+z+")"); + + Phrase phrase = Phrase.LOOKUP_BLOCK; + String selector = Selector.FIRST; + String action = "a:block"; + if (actions.contains(4) || actions.contains(5) || actions.contains(11) || amount > -1) { + byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1); + String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount); + + if (daction == 2 || daction == 3) { + phrase = Phrase.LOOKUP_ITEM; // {picked up|dropped} + selector = (daction != 2 ? Selector.FIRST : Selector.SECOND); + tag = (daction != 2 ? Color.GREEN + "+" : Color.RED + "-"); + action = "a:item"; + } + else if (daction == 4 || daction == 5) { + phrase = Phrase.LOOKUP_STORAGE; // {deposited|withdrew} + selector = (daction != 4 ? Selector.FIRST : Selector.SECOND); + tag = (daction != 4 ? Color.RED + "-" : Color.GREEN + "+"); + action = "a:item"; + } + else if (daction == 6 || daction == 7) { + phrase = Phrase.LOOKUP_PROJECTILE; // {threw|shot} + selector = (daction != 7 ? Selector.FIRST : Selector.SECOND); + tag = Color.RED + "-"; + action = "a:item"; + } + else { + phrase = Phrase.LOOKUP_CONTAINER; // {added|removed} + selector = (daction != 0 ? Selector.FIRST : Selector.SECOND); + tag = (daction != 0 ? Color.GREEN + "+" : Color.RED + "-"); + action = "a:container"; + } - action = (actions.size() == 0 ? " (" + action + ")" : ""); - Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + Color.GREY + Color.ITALIC + action); + Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector)); + PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), phrase, selector, dplayer, dname, (tag.contains("+") ? 1 : -1), dataX, dataY, dataZ, wid, rbd, action.contains("container"), tag.contains("+")); + } + else { + if (daction == 2 || daction == 3) { + phrase = Phrase.LOOKUP_INTERACTION; // {clicked|killed} + selector = (daction != 3 ? Selector.FIRST : Selector.SECOND); + tag = (daction != 3 ? Color.WHITE + "-" : Color.RED + "-"); + action = (daction == 2 ? "a:click" : "a:kill"); + } + else { + phrase = Phrase.LOOKUP_BLOCK; // {placed|broke} + selector = (daction != 0 ? Selector.FIRST : Selector.SECOND); + tag = (daction != 0 ? Color.GREEN + "+" : Color.RED + "-"); + } + + Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, Color.DARK_AQUA + rbd + dname + Color.WHITE, selector)); + PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), phrase, selector, dplayer, dname, (tag.contains("+") ? 1 : -1), dataX, dataY, dataZ, wid, rbd, false, tag.contains("+")); + } + + action = (actions.size() == 0 ? " (" + action + ")" : ""); + Chat.sendComponent(player, Color.WHITE + leftPadding + Color.GREY + "^ " + ChatUtils.getCoordinates(command.getName(), wid, dataX, dataY, dataZ, true, true) + Color.GREY + Color.ITALIC + action); + } } - } - if (rows > displayResults) { - int total_pages = (int) Math.ceil(rows / (displayResults + 0.0)); - if (actions.contains(6) || actions.contains(7) || actions.contains(9) || (actions.contains(4) && actions.contains(11))) { - Chat.sendMessage(player, "-----"); + if (rows > displayResults) { + int total_pages = (int) Math.ceil(rows / (displayResults + 0.0)); + if (actions.contains(6) || actions.contains(7) || actions.contains(9) || (actions.contains(4) && actions.contains(11))) { + Chat.sendMessage(player, "-----"); + } + Chat.sendComponent(player, ChatUtils.getPageNavigation(command.getName(), page, total_pages)); } - Chat.sendComponent(player, ChatUtils.getPageNavigation(command.getName(), page, total_pages)); } - } - else if (rows > 0) { - Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS_PAGE, Selector.FIRST)); - } - else { - Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); + else if (rows > 0) { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS_PAGE, Selector.FIRST)); + } + else { + Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_RESULTS)); + } } } else { @@ -467,4 +542,142 @@ else if (rows > 0) { ConfigHandler.lookupThrottle.put(player.getName(), new Object[] { false, System.currentTimeMillis() }); } + + // Helper method to map String[] data to Map + private Map mapResultData(String[] data, List actions, Connection connection) { + Map result = new HashMap<>(); + try { + long time = Long.parseLong(data[0]); + result.put("timestamp", time); + result.put("player", data[1]); + + if (actions.contains(6) || actions.contains(7)) { // Chat/Command + result.put("type", actions.contains(6) ? "chat" : "command"); + result.put("message", data[2]); + result.put("world", WorldUtils.getWorldName(Integer.parseInt(data[3]))); + result.put("x", Integer.parseInt(data[4])); + result.put("y", Integer.parseInt(data[5])); + result.put("z", Integer.parseInt(data[6])); + } + else if (actions.contains(8)) { // Session (Login/Logout) + result.put("type", "session"); + result.put("action", Integer.parseInt(data[6]) != 0 ? "login" : "logout"); + result.put("world", WorldUtils.getWorldName(Integer.parseInt(data[2]))); + result.put("x", Integer.parseInt(data[3])); + result.put("y", Integer.parseInt(data[4])); + result.put("z", Integer.parseInt(data[5])); + } + else if (actions.contains(9)) { // Username change + result.put("type", "username_change"); + String uuid = data[1]; + String oldUser = ConfigHandler.uuidCacheReversed.get(uuid); // May need loading if not cached + result.put("uuid", uuid); + result.put("old_username", oldUser != null ? oldUser : "(unknown)"); + result.put("new_username", data[2]); + } + else if (actions.contains(10)) { // Sign text + result.put("type", "sign"); + result.put("world", WorldUtils.getWorldName(Integer.parseInt(data[2]))); + result.put("x", Integer.parseInt(data[3])); + result.put("y", Integer.parseInt(data[4])); + result.put("z", Integer.parseInt(data[5])); + result.put("text", data[6]); + } + else if (actions.contains(4) && actions.contains(11)) { // Inventory transactions + result.put("type", "inventory"); + result.put("x", Integer.parseInt(data[2])); + result.put("y", Integer.parseInt(data[3])); + result.put("z", Integer.parseInt(data[4])); + int matId = Integer.parseInt(data[5]); + int matData = Integer.parseInt(data[6]); + int actionCode = Integer.parseInt(data[7]); + int amount = Integer.parseInt(data[10]); + result.put("world", WorldUtils.getWorldName(Integer.parseInt(data[9]))); + result.put("rolled_back", (Integer.parseInt(data[8]) == 2 || Integer.parseInt(data[8]) == 3)); + Material material = ItemUtils.itemFilter(MaterialUtils.getType(matId), (Integer.parseInt(data[13]) == 0)); + result.put("material", material != null ? material.name() : "ID:" + matId); + result.put("material_data", matData); + result.put("amount", amount); + result.put("action", mapInventoryAction(actionCode)); + // Metadata (byte[]) is skipped for JSON simplicity unless needed + } + else { // Block/Interaction/Item (non-inventory) + result.put("x", Integer.parseInt(data[2])); + result.put("y", Integer.parseInt(data[3])); + result.put("z", Integer.parseInt(data[4])); + int matId = Integer.parseInt(data[5]); + int matData = Integer.parseInt(data[6]); + int actionCode = Integer.parseInt(data[7]); + int amount = Integer.parseInt(data[10]); // Amount is relevant for items + result.put("world", WorldUtils.getWorldName(Integer.parseInt(data[9]))); + result.put("rolled_back", (Integer.parseInt(data[8]) == 1 || Integer.parseInt(data[8]) == 3)); + + if (actionCode == 3 && !actions.contains(11) && amount == -1) { // Kill action + result.put("type", "kill"); + if (matId == 0) { // Player kill + if (ConfigHandler.playerIdCacheReversed.get(matData) == null) { + UserStatement.loadName(connection, matData); // Ensure name is loaded + } + result.put("killed_player", ConfigHandler.playerIdCacheReversed.get(matData)); + } else { + result.put("killed_entity", EntityUtils.getEntityType(matId).name()); + } + } else if (actionCode == 2 && !actions.contains(11) && amount == -1) { // Click action + result.put("type", "interaction"); + result.put("clicked_material", StringUtils.nameFilter(MaterialUtils.getType(matId).name().toLowerCase(Locale.ROOT), matData)); + } else if (actions.contains(4) || actions.contains(5) || actions.contains(11) || amount > -1) { // Container/Item actions (non-inventory) + result.put("type", "item"); // Simplified type for export + result.put("action", mapItemAction(actionCode)); + result.put("material", StringUtils.nameFilter(MaterialUtils.getType(matId).name().toLowerCase(Locale.ROOT), matData)); + result.put("amount", amount); + // Metadata skipped + } else { // Block actions + result.put("type", "block"); + result.put("action", actionCode == 1 ? "place" : "break"); + result.put("material", StringUtils.nameFilter(MaterialUtils.getType(matId).name().toLowerCase(Locale.ROOT), matData)); + } + } + return result; + } + catch (Exception e) { + System.err.println("Error mapping result data: " + String.join(", ", data)); + e.printStackTrace(); + return null; // Skip this entry if mapping fails + } + } + + // Helper for inventory action codes + private String mapInventoryAction(int actionCode) { + switch (actionCode) { + case 0: return "added_to_container"; + case 1: return "removed_from_container"; + case 2: return "added_item"; // e.g., picked up + case 3: return "removed_item"; // e.g., dropped + case 4: return "deposited_storage"; + case 5: return "withdrew_storage"; + case 6: return "threw_projectile"; + case 7: return "shot_projectile"; + case ItemLogger.ITEM_BREAK: return "item_break"; + case ItemLogger.ITEM_DESTROY: return "item_destroy"; + case ItemLogger.ITEM_CREATE: return "item_create"; + case ItemLogger.ITEM_SELL: return "item_sell"; + case ItemLogger.ITEM_BUY: return "item_buy"; + default: return "unknown_" + actionCode; + } + } + + // Helper for non-inventory item action codes + private String mapItemAction(int actionCode) { + switch (actionCode) { + case 0: return "added_to_container"; // From block context + case 1: return "removed_from_container"; // From block context + case 2: return "picked_up"; + case 3: return "dropped"; + case 4: return "deposited_storage"; + case 5: return "withdrew_storage"; + case 6: return "threw_projectile"; + case 7: return "shot_projectile"; + default: return "unknown_" + actionCode; + } + } } diff --git a/src/main/java/net/coreprotect/language/Phrase.java b/src/main/java/net/coreprotect/language/Phrase.java index 64d20f00..65bfba38 100644 --- a/src/main/java/net/coreprotect/language/Phrase.java +++ b/src/main/java/net/coreprotect/language/Phrase.java @@ -220,7 +220,13 @@ public enum Phrase { VERSION_NOTICE, VERSION_INCOMPATIBLE, VERSION_REQUIRED, - WORLD_NOT_FOUND; + WORLD_NOT_FOUND, + + // Export related phrases + EXPORT_GENERATING, + EXPORT_SUCCESS, + EXPORT_FAILURE, + EXPORT_ERROR; final private static Set HEADERS = new HashSet<>(Arrays.asList(Phrase.CONTAINER_HEADER, Phrase.HELP_HEADER, Phrase.INTERACTIONS_HEADER, Phrase.LOOKUP_HEADER, Phrase.SIGN_HEADER, Phrase.UPDATE_HEADER)); final private static Set COLORS = new HashSet<>(Arrays.asList(Color.WHITE, Color.DARK_AQUA)); diff --git a/src/main/java/net/coreprotect/services/PluginInitializationService.java b/src/main/java/net/coreprotect/services/PluginInitializationService.java index 1d39a9d8..325d115a 100644 --- a/src/main/java/net/coreprotect/services/PluginInitializationService.java +++ b/src/main/java/net/coreprotect/services/PluginInitializationService.java @@ -88,11 +88,15 @@ public static boolean initializePlugin(CoreProtect plugin) { * The CoreProtect plugin instance */ private static void registerCommands(JavaPlugin plugin) { - plugin.getCommand("coreprotect").setExecutor(CommandHandler.getInstance()); + // Cast JavaPlugin to CoreProtect, assuming it's safe in this context + CoreProtect coreProtectPlugin = (CoreProtect) plugin; + + // Pass the CoreProtect instance to CommandHandler + plugin.getCommand("coreprotect").setExecutor(CommandHandler.getInstance(coreProtectPlugin)); plugin.getCommand("coreprotect").setTabCompleter(new TabHandler()); - plugin.getCommand("core").setExecutor(CommandHandler.getInstance()); + plugin.getCommand("core").setExecutor(CommandHandler.getInstance(coreProtectPlugin)); plugin.getCommand("core").setTabCompleter(new TabHandler()); - plugin.getCommand("co").setExecutor(CommandHandler.getInstance()); + plugin.getCommand("co").setExecutor(CommandHandler.getInstance(coreProtectPlugin)); plugin.getCommand("co").setTabCompleter(new TabHandler()); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1f61249d..419248c1 100755 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: CoreProtect main: net.coreprotect.CoreProtect version: ${project.version} -branch: ${project.branch} +branch: development api-version: 1.13 folia-supported: true website: http://coreprotect.net