From c1d4e126a3ca8fe0b5dcccfecba5975e32d6f13c Mon Sep 17 00:00:00 2001 From: speed <43330808+speedxx@users.noreply.github.com> Date: Mon, 30 Nov 2020 18:17:27 -0500 Subject: [PATCH] add block inspector for ops * adds /inspect (or /ins) as a /co i replacement command for OPs. container and sign logging are still a WIP for this command. * replaces "Rank must be Trial Mod or higher." with "Rank must be Admin or higher." in slconfig * replaces "scripthead is the owner of TotalFreedom." with "Wild1145 is the owner of TotalFreedom." in the config --- .../bridge/CoreProtectBridge.java | 225 ++++++++++++++++++ .../command/Command_forcekill.java | 3 +- .../command/Command_inspect.java | 69 ++++++ .../command/Command_slconfig.java | 2 +- .../totalfreedommod/player/PlayerData.java | 9 + .../totalfreedommod/sql/SQLite.java | 5 +- .../totalfreedommod/util/FUtil.java | 47 +++- src/main/resources/config.yml | 2 +- 8 files changed, 350 insertions(+), 12 deletions(-) create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java index 47f1a83c..85201418 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java @@ -6,16 +6,30 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.text.DecimalFormat; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import me.totalfreedom.totalfreedommod.FreedomService; import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; import net.coreprotect.CoreProtect; import net.coreprotect.CoreProtectAPI; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @@ -26,6 +40,10 @@ public class CoreProtectBridge extends FreedomService private final List tables = Arrays.asList("co_sign", "co_session", "co_container", "co_block"); + private final HashMap cooldown = new HashMap<>(); + + public static Map> HISTORY_MAP = new HashMap<>(); + private BukkitTask wiper; @Override @@ -218,4 +236,211 @@ public class CoreProtectBridge extends FreedomService server.shutdown(); } } + + public static Long getSecondsLeft(long prevTime, int timeAdd) + { + return prevTime / 1000L + timeAdd - System.currentTimeMillis() / 1000L; + } + + // Unix timestamp converter taken from Functions class in CoreProtect, not my code + public static String getTimeAgo(int logTime, int currentTime) + { + StringBuilder message = new StringBuilder(); + double timeSince = (double)currentTime - ((double)logTime + 0.0D); + timeSince /= 60.0D; + if (timeSince < 60.0D) + { + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/m ago"); + } + + if (message.length() == 0) + { + timeSince /= 60.0D; + if (timeSince < 24.0D) + { + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/h ago"); + } + } + + if (message.length() == 0) + { + timeSince /= 24.0D; + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/d ago"); + } + + return message.toString(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerInteract(PlayerInteractEvent event) + { + Player player = event.getPlayer(); + PlayerData data = plugin.pl.getData(player); + Block block = event.getClickedBlock(); + final CoreProtectAPI coreProtect = getCoreProtectAPI(); + + if (data.hasInspection()) + { + if (event.getAction() == Action.LEFT_CLICK_BLOCK) + { + if (block != null) + { + event.setCancelled(true); + List lookup = coreProtect.blockLookup(block, -1); + + int cooldownTime = 3; + + if (cooldown.containsKey(player.getName())) + { + long secondsLeft = getSecondsLeft(cooldown.get(player.getName()), cooldownTime); + if (secondsLeft > 0L) + { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + String.valueOf(secondsLeft) + " seconds left before next query."); + return; + } + } + + if (!plugin.sl.isStaff(player)) + { + cooldown.put(player.getName(), System.currentTimeMillis()); + } + + if (lookup != null) + { + if (lookup.isEmpty()) + { + player.sendMessage(net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector " + ChatColor.WHITE + "- " + "No block data found for this location"); + return; + } + + HISTORY_MAP.remove(event.getPlayer()); + HISTORY_MAP.put(event.getPlayer(), new FUtil.PaginationList<>(10)); + FUtil.PaginationList paged = HISTORY_MAP.get(event.getPlayer()); + + player.sendMessage("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- " + + ChatColor.GRAY + "(x" + block.getX() + "/" + "y" + block.getY() + "/" + "z" + block.getZ() + ")"); + + for (String[] value : lookup) + { + CoreProtectAPI.ParseResult result = coreProtect.parseResult(value); + BlockData bl = result.getBlockData(); + + String s; + String st = ""; + + if (result.getActionString().equals("Placement")) + { + s = " placed "; + } + else + { + s = " broke "; + } + + if (result.isRolledBack()) + { + st += "§m"; + } + + int time = (int)(System.currentTimeMillis() / 1000L); + + paged.add(ChatColor.GRAY + getTimeAgo(result.getTime(), time) + ChatColor.WHITE + " - " + net.md_5.bungee.api.ChatColor.of("#30ade4") + + st + result.getPlayer() + ChatColor.WHITE + st + s + net.md_5.bungee.api.ChatColor.of("#30ade4") + st + bl.getMaterial().toString().toLowerCase()); + } + + List page = paged.getPage(1); + for (String entries : page) + { + player.sendMessage(entries); + } + + player.sendMessage("Page 1/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history "); + } + } + } + else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) + { + if (block != null) + { + if (data.hasInspection()) + { + BlockState placedBlock = block.getRelative(event.getBlockFace()).getState(); + event.setCancelled(true); + List lookup = coreProtect.blockLookup(placedBlock.getBlock(), -1); + + int cooldownTime = 3; + + if (cooldown.containsKey(player.getName())) + { + long secondsLeft = getSecondsLeft(cooldown.get(player.getName()), cooldownTime); + if (secondsLeft > 0L) + { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + String.valueOf(secondsLeft) + " seconds left before next query."); + return; + } + } + + if (!plugin.sl.isStaff(player)) + { + cooldown.put(player.getName(), System.currentTimeMillis()); + } + + if (lookup != null) + { + if (lookup.isEmpty()) + { + player.sendMessage(net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector " + ChatColor.WHITE + "- " + "No block data found for this location"); + return; + } + + HISTORY_MAP.remove(event.getPlayer()); + HISTORY_MAP.put(event.getPlayer(), new FUtil.PaginationList<>(10)); + FUtil.PaginationList paged = HISTORY_MAP.get(event.getPlayer()); + + player.sendMessage("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- " + + ChatColor.GRAY + "(x" + block.getX() + "/" + "y" + block.getY() + "/" + "z" + block.getZ() + ")"); + + for (String[] value : lookup) + { + CoreProtectAPI.ParseResult result = coreProtect.parseResult(value); + BlockData bl = result.getBlockData(); + + String s; + String st = ""; + + if (result.getActionString().equals("Placement")) + { + s = " placed "; + } + else + { + s = " broke "; + } + + if (result.isRolledBack()) + { + st += "§m"; + } + + int time = (int)(System.currentTimeMillis() / 1000L); + + paged.add(ChatColor.GRAY + getTimeAgo(result.getTime(), time) + ChatColor.WHITE + " - " + net.md_5.bungee.api.ChatColor.of("#30ade4") + + st + result.getPlayer() + ChatColor.WHITE + st + s + net.md_5.bungee.api.ChatColor.of("#30ade4") + st + bl.getMaterial().toString().toLowerCase()); + } + + List page = paged.getPage(1); + for (String entries : page) + { + player.sendMessage(entries); + } + + player.sendMessage("Page 1/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history "); + } + } + } + } + } + } } \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java index a7c4d1ce..2b747494 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java @@ -32,7 +32,6 @@ public class Command_forcekill extends FreedomCommand } player.setHealth(0); - return true; } -} +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java new file mode 100644 index 00000000..81c6001a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java @@ -0,0 +1,69 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.bridge.CoreProtectBridge; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Block inspector tool for operators", usage = "/ [history] ", aliases = "ins") +public class Command_inspect extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + PlayerData playerData = plugin.pl.getData(playerSender); + playerData.setInspect(!playerData.hasInspection()); + plugin.pl.save(playerData); + msg("Block inspector " + (playerData.hasInspection() ? "enabled." : "disabled.")); + return true; + } + + if (args[0].equalsIgnoreCase("history")) + { + int pageIndex = 1; + + if (args.length >= 2) + { + try + { + pageIndex = Integer.parseInt(args[1]); + } + catch (NumberFormatException e) + { + sender.sendMessage(ChatColor.RED + "Invalid number"); + } + } + + FUtil.PaginationList paged = CoreProtectBridge.HISTORY_MAP.get(playerSender); + if (paged != null) + { + if (pageIndex < 1 || pageIndex > paged.getPageCount()) + { + sender.sendMessage(ChatColor.RED + "Not a valid page number"); + return true; + } + + sender.sendMessage("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- "); + + List page = paged.getPage(pageIndex); + for (String entries : page) + { + sender.sendMessage(entries); + } + + sender.sendMessage("Page " + pageIndex + "/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history "); + return true; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_slconfig.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_slconfig.java index d1c619a7..9915f401 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_slconfig.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_slconfig.java @@ -85,7 +85,7 @@ public class Command_slconfig extends FreedomCommand if (!rank.isAtLeast(Rank.ADMIN)) { - msg("Rank must be Trial Mod or higher.", ChatColor.RED); + msg("Rank must be Admin or higher.", ChatColor.RED); return true; } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java index 39cdbf13..6bb330f4 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java @@ -53,6 +53,8 @@ public class PlayerData @Getter @Setter private String loginMessage; + @Setter + private Boolean inspect = false; public PlayerData(ResultSet resultSet) { @@ -77,6 +79,7 @@ public class PlayerData displayDiscord = resultSet.getBoolean("display_discord"); redditUsername = resultSet.getString("reddit_username"); loginMessage = resultSet.getString("login_message"); + inspect = resultSet.getBoolean("inspect"); } catch (SQLException e) { @@ -231,6 +234,11 @@ public class PlayerData return masterBuilder; } + public boolean hasInspection() + { + return inspect; + } + public Map toSQLStorable() { Map map = new HashMap() @@ -250,6 +258,7 @@ public class PlayerData put("display_discord", displayDiscord); put("reddit_username", redditUsername); put("login_message", loginMessage); + put("inspect", inspect); }}; return map; } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java b/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java index ed2d02d7..16d15321 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java @@ -93,7 +93,7 @@ public class SQLite extends FreedomService { try { - connection.createStatement().execute("CREATE TABLE `players` (`username` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `notes` VARCHAR, `tag` VARCHAR, `discord_id` VARCHAR, `backup_codes` VARCHAR, `donator` BOOLEAN NOT NULL, `master_builder` BOOLEAN NOT NULL,`verification` BOOLEAN NOT NULL, `ride_mode` VARCHAR NOT NULL, `coins` INT, `items` VARCHAR, `total_votes` INT NOT NULL, `display_discord` BOOLEAN NOT NULL, `reddit_username` VARCHAR, `login_message` VARCHAR);"); + connection.createStatement().execute("CREATE TABLE `players` (`username` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `notes` VARCHAR, `tag` VARCHAR, `discord_id` VARCHAR, `backup_codes` VARCHAR, `master_builder` BOOLEAN NOT NULL,`verification` BOOLEAN NOT NULL, `ride_mode` VARCHAR NOT NULL, `coins` INT, `items` VARCHAR, `total_votes` INT NOT NULL, `display_discord` BOOLEAN NOT NULL, `reddit_username` VARCHAR, `login_message` VARCHAR, `inspect` BOOLEAN NOT NULL);"); } catch (SQLException e) { @@ -270,7 +270,7 @@ public class SQLite extends FreedomService { try { - PreparedStatement statement = connection.prepareStatement("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + PreparedStatement statement = connection.prepareStatement("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); statement.setString(1, player.getName()); statement.setString(2, FUtil.listToString(player.getIps())); statement.setString(3, FUtil.listToString(player.getNotes())); @@ -286,6 +286,7 @@ public class SQLite extends FreedomService statement.setBoolean(13, player.doesDisplayDiscord()); statement.setString(14, player.getRedditUsername()); statement.setString(15, player.getLoginMessage()); + statement.setBoolean(16, player.hasInspection()); statement.executeUpdate(); } catch (SQLException e) diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java index 931ee0fb..e375f1d8 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java @@ -190,8 +190,9 @@ public class FUtil /** * A way to get a sublist with a page index and a page size. - * @param list A list of objects that should be split into pages. - * @param size The size of the pages. + * + * @param list A list of objects that should be split into pages. + * @param size The size of the pages. * @param index The page index, if outside of bounds error will be thrown. The page index starts at 0 as with all lists. * @return A list of objects that is the page that has been selected from the previous last parameter. */ @@ -254,7 +255,7 @@ public class FUtil return null; } - public static Response sendRequest(String endpoint, String method, Listheaders, String body) throws IOException + public static Response sendRequest(String endpoint, String method, List headers, String body) throws IOException { URL url = new URL(endpoint); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); @@ -800,13 +801,13 @@ public class FUtil { c1values[i] = Math.round(c1values[i] + factor * (c2values[i] - c1values[i])); } - return Color.fromRGB((int) c1values[0], (int) c1values[1], (int) c1values[2]); + return Color.fromRGB((int)c1values[0], (int)c1values[1], (int)c1values[2]); } public static boolean isValidIPv4(String ip) { if (ip.matches("^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$") - || ip.matches("^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([*])\\.([*])$")) + || ip.matches("^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([*])\\.([*])$")) { return true; } @@ -868,4 +869,38 @@ public class FUtil } }.runTaskLater(TotalFreedomMod.getPlugin(), delay); } -} + + public static class PaginationList extends ArrayList + { + private final int epp; + + public PaginationList(int epp) + { + super(); + this.epp = epp; + } + + @SafeVarargs + public PaginationList(int epp, T... elements) + { + super(Arrays.asList(elements)); + this.epp = epp; + } + + public int getPageCount() + { + return (int)Math.ceil((double)size() / (double)epp); + } + + public List getPage(int page) + { + if (page < 1 || page > getPageCount()) + { + return null; + } + int startIndex = (page - 1) * epp; + int endIndex = Math.min(startIndex + (epp - 1), this.size() - 1); + return subList(startIndex, endIndex + 1); + } + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9d68be28..a6fb275f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -418,7 +418,7 @@ announcer: - 'Save your buildings via WorldEdit! http://totalfreedom.me for more information!' - 'You may contact TotalFreedom support on Twitter! https://tiny.re/tfsupport' - 'You may download TotalFreedomMod here: https://tiny.re/tfm+' - - 'scripthead is the owner of TotalFreedom.' + - 'Wild1145 is the owner of TotalFreedom.' - 'markbyron is the founder of TotalFreedom.' - 'Server lagging? Check the lag via "/tps"' - 'You are allowed to record and stream videos on TotalFreedom.'