diff --git a/pom.xml b/pom.xml index 0a1667e..28f8df3 100644 --- a/pom.xml +++ b/pom.xml @@ -56,11 +56,6 @@ - - LocalRepo - Local Repo - file:/Users/chrisblakey/.m2/repository/ - papermc https://repo.papermc.io/repository/maven-public/ diff --git a/src/main/java/net/supremesurvival/supremecore/commonUtils/fileHandler/FileHandler.java b/src/main/java/net/supremesurvival/supremecore/commonUtils/fileHandler/FileHandler.java index ac7ede6..0020567 100644 --- a/src/main/java/net/supremesurvival/supremecore/commonUtils/fileHandler/FileHandler.java +++ b/src/main/java/net/supremesurvival/supremecore/commonUtils/fileHandler/FileHandler.java @@ -9,55 +9,77 @@ public class FileHandler { private static Plugin plugin; - public FileHandler (Plugin pl){ + + public FileHandler(Plugin pl) { plugin = pl; } + final static String handle = "FileHandler"; - public static File getDataFile(String filePath){ + + public static File getDataFile(String filePath) { Logger.sendMessage(plugin.getDataFolder().toString(), Logger.LogType.INFO, handle); File dataFile = new File(plugin.getDataFolder(), filePath); - Logger.sendMessage(dataFile.getPath(), Logger.LogType.INFO,handle); + Logger.sendMessage(dataFile.getPath(), Logger.LogType.INFO, handle); return dataFile; } - public static HashMap loadMoralityData(UUID player, HashMap hashMap, File file){ - try (BufferedReader reader = new BufferedReader(new FileReader(file))){ + public static HashMap loadMoralityData(UUID player, HashMap hashMap, File file) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; - while ((line=reader.readLine())!=null){ + while ((line = reader.readLine()) != null) { + if (line == null || line.isBlank()) continue; + String[] parts = line.split(":"); - UUID playerUUID = UUID.fromString(parts[0]); - if (playerUUID.equals(player)){ - String data = parts[1]; - int dataInt = Integer.parseInt(data); + if (parts.length < 2) { + Logger.sendMessage("Skipping malformed morality line: " + line, Logger.LogType.WARN, handle); + continue; + } + + try { + UUID playerUUID = UUID.fromString(parts[0]); + if (!playerUUID.equals(player)) continue; + + int dataInt = Integer.parseInt(parts[1]); MoralPlayer moralPlayer = new MoralPlayer(playerUUID, dataInt); - hashMap.put(playerUUID,moralPlayer); + hashMap.put(playerUUID, moralPlayer); Logger.sendMessage("Loaded Single Data for " + playerUUID + " into Hashmap", Logger.LogType.INFO, handle); return hashMap; + } catch (IllegalArgumentException ex) { + Logger.sendMessage("Skipping invalid morality entry: " + line, Logger.LogType.WARN, handle); } } - //playerdata not located - } - catch (IOException e){ + // playerdata not located + } catch (IOException e) { Logger.sendMessage(e.getMessage(), Logger.LogType.ERR, handle); } return null; } - public static HashMap loadAllMoralityData(File file, HashMap hashmap){ - try (BufferedReader reader = new BufferedReader(new FileReader(file))){ + public static HashMap loadAllMoralityData(File file, HashMap hashmap) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; - while ((line=reader.readLine())!=null){ + while ((line = reader.readLine()) != null) { + if (line == null || line.isBlank()) continue; + String[] parts = line.split(":"); - UUID playerUUID = UUID.fromString(parts[0]); - String data = parts [1]; - int dataInt = Integer.parseInt(data); - MoralPlayer moralPlayer = new MoralPlayer(playerUUID, dataInt); - hashmap.put(playerUUID, moralPlayer); + if (parts.length < 2) { + Logger.sendMessage("Skipping malformed morality line: " + line, Logger.LogType.WARN, handle); + continue; + } + + try { + UUID playerUUID = UUID.fromString(parts[0]); + int dataInt = Integer.parseInt(parts[1]); + MoralPlayer moralPlayer = new MoralPlayer(playerUUID, dataInt); + hashmap.put(playerUUID, moralPlayer); + } catch (IllegalArgumentException ex) { + Logger.sendMessage("Skipping invalid morality entry: " + line, Logger.LogType.WARN, handle); + } } return hashmap; -}catch (IOException e){ + } catch (IOException e) { Logger.sendMessage(e.getMessage(), Logger.LogType.ERR, handle); } return null; } -} \ No newline at end of file +} diff --git a/src/main/java/net/supremesurvival/supremecore/commonUtils/placeholder/SupremePlaceholder.java b/src/main/java/net/supremesurvival/supremecore/commonUtils/placeholder/SupremePlaceholder.java index 096356e..49c310c 100644 --- a/src/main/java/net/supremesurvival/supremecore/commonUtils/placeholder/SupremePlaceholder.java +++ b/src/main/java/net/supremesurvival/supremecore/commonUtils/placeholder/SupremePlaceholder.java @@ -6,36 +6,36 @@ import net.supremesurvival.supremecore.morality.MoralityPlaceholderExpansion; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -//This class will serve as a single place for us to return placeholder responses. Placeholder variables will be loaded into the switch case below. -//Shouldnt really get too large, even if it did i dont think it'd cause issues. If server sets on fire will revisit. + +// This class serves as a single place for us to return placeholder responses. +// Placeholder variables will be loaded into the switch case below. public class SupremePlaceholder { public static SupremeCore pl; final static String handle = "Supreme Placeholder"; - public static String onRequest(Player player, String string){ - Logger.sendMessage(string, Logger.LogType.INFO, "S-PAPI"); - switch (string) { - case "morality" -> { - Logger.sendMessage("returned Morality", Logger.LogType.INFO, handle); - return String.valueOf(Morality.getMorality(player)); - } + + public static String onRequest(Player player, String string) { + if (player == null || string == null) { + return null; + } + + return switch (string) { + case "morality" -> String.valueOf(Morality.getMorality(player)); case "standing" -> { - Logger.sendMessage("returned Morality", Logger.LogType.INFO, handle); String standingString = Morality.getMoralStanding(player); standingString = standingString.substring(0, 1).toUpperCase() + standingString.substring(1).toLowerCase(); - return standingString; - } - default -> { - return null; + yield standingString; } - } + default -> null; + }; } - public static void register(){ - if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI")!= null){ + + public static void register() { + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { new MoralityPlaceholderExpansion().register(); } } - public static void enable(SupremeCore plugin){ + public static void enable(SupremeCore plugin) { pl = plugin; Logger.sendMessage("Enabled", Logger.LogType.INFO, "SupremePAPI"); } diff --git a/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateCommand.java b/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateCommand.java index af4bd6b..a67312c 100644 --- a/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateCommand.java +++ b/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateCommand.java @@ -1,20 +1,23 @@ package net.supremesurvival.supremecore.realestate; +import net.supremesurvival.supremecore.commonUtils.fileHandler.ConfigUtility; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.List; -import java.util.Locale; +import java.util.*; public class RealEstateCommand implements CommandExecutor { private static final int PAGE_SIZE = 8; private final RealEstateManager manager = new RealEstateManager(); + private final Map lastViewUse = new HashMap<>(); @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { @@ -87,6 +90,24 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return true; } + RealEstateSecurityPolicy policy = loadPolicy(); + if (!policy.allowedWorlds.isEmpty() && !policy.allowedWorlds.contains(listing.worldName().toLowerCase(Locale.ROOT))) { + player.sendMessage(ChatColor.RED + "Viewing is disabled for that world."); + return true; + } + + if (policy.cooldownSeconds > 0 && !player.hasPermission("realestate.bypasscooldown")) { + long now = System.currentTimeMillis(); + long last = lastViewUse.getOrDefault(player.getUniqueId(), 0L); + long waitMs = (policy.cooldownSeconds * 1000L) - (now - last); + if (waitMs > 0) { + long waitSeconds = (waitMs + 999L) / 1000L; + player.sendMessage(ChatColor.RED + "You must wait " + waitSeconds + "s before using /realestate view again."); + return true; + } + lastViewUse.put(player.getUniqueId(), now); + } + Location target = manager.resolveTeleportLocation(listing); if (target == null) { player.sendMessage(ChatColor.RED + "Could not resolve a safe viewing location for that listing."); @@ -102,6 +123,26 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return true; } + private RealEstateSecurityPolicy loadPolicy() { + FileConfiguration config = ConfigUtility.getModuleConfig("RealEstate"); + int cooldownSeconds = Math.max(0, config.getInt("view.cooldown-seconds", 15)); + List worlds = new ArrayList<>(); + + ConfigurationSection section = config.getConfigurationSection("view"); + if (section != null) { + worlds = section.getStringList("world-allowlist"); + } + + Set allowed = new HashSet<>(); + for (String world : worlds) { + if (world != null && !world.isBlank()) { + allowed.add(world.toLowerCase(Locale.ROOT)); + } + } + + return new RealEstateSecurityPolicy(cooldownSeconds, allowed); + } + private void sendHelp(Player player) { player.sendMessage(ChatColor.YELLOW + "/realestate list [town] [page]"); player.sendMessage(ChatColor.YELLOW + "/realestate view "); @@ -115,4 +156,6 @@ private boolean isInteger(String s) { return false; } } + + private record RealEstateSecurityPolicy(int cooldownSeconds, Set allowedWorlds) {} } diff --git a/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateManager.java b/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateManager.java index 566243c..368de19 100644 --- a/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateManager.java +++ b/src/main/java/net/supremesurvival/supremecore/realestate/RealEstateManager.java @@ -1,5 +1,6 @@ package net.supremesurvival.supremecore.realestate; +import net.supremesurvival.supremecore.commonUtils.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -17,6 +18,7 @@ public class RealEstateManager { private static final long CACHE_MS = 20_000L; + private static final String HANDLE = "RealEstate"; private List cachedListings = new ArrayList<>(); private long lastRefresh = 0L; @@ -117,17 +119,18 @@ private List fetchTownyListings() { out.sort(Comparator.comparingDouble(RealEstateListing::price)); return out; - } catch (Throwable ignored) { + } catch (ReflectiveOperationException ex) { + Logger.sendMessage("Towny reflection lookup failed: " + ex.getMessage(), Logger.LogType.WARN, HANDLE); return Collections.emptyList(); } } - private static Object invoke(Object target, String method) throws Exception { + private static Object invoke(Object target, String method) throws ReflectiveOperationException { Method m = target.getClass().getMethod(method); return m.invoke(target); } - private static Object invokeStatic(Class target, String method) throws Exception { + private static Object invokeStatic(Class target, String method) throws ReflectiveOperationException { Method m = target.getMethod(method); return m.invoke(null); } @@ -137,7 +140,7 @@ private static Object invokeSafe(Object target, String method) { try { Method m = target.getClass().getMethod(method); return m.invoke(target); - } catch (Throwable ignored) { + } catch (ReflectiveOperationException ignored) { return null; } } diff --git a/src/main/resources/RealEstate/config.yml b/src/main/resources/RealEstate/config.yml new file mode 100644 index 0000000..9be98a8 --- /dev/null +++ b/src/main/resources/RealEstate/config.yml @@ -0,0 +1,10 @@ +view: + # Cooldown between /realestate view teleports. + cooldown-seconds: 15 + + # Leave empty to allow all worlds. + # Example: + # world-allowlist: + # - world + # - world_nether + world-allowlist: [] diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a0e43e7..2bc423b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -43,6 +43,9 @@ permissions: realestate.view: description: teleport to view a Towny listing default: true + realestate.bypasscooldown: + description: bypass /realestate view cooldown checks + default: op commands: HorseInfo: description: Your description