diff --git a/README.md b/README.md index 720c8fc..2cc1893 100644 --- a/README.md +++ b/README.md @@ -16,27 +16,55 @@ Available now on [Modrinth](https://modrinth.com/plugin/postoffice), [CurgeForge ## How does the plugin work? - Upload the JAR to your server and reboot -- Rename barrels in an anvil to "pobox" - if you want to use a different name, be sure to change this in the config and use /postoffice reload to update your config file. +- Make any desired changes to the config file (not essential), and then type /postoffice reload' as an op. - Place your barrels in your amazing Post Office build. -- Place a sign on the front of the barrel and type the barrel owners name on the second line, this then becomes the owner of the post box. +- Place a blank sign on the front of the barrel. +- While looking at the sign, type '/postoffice register' to register the barrel in the config file. +- Again while looking at the sign, if permitted your players can type '/postoffice claim' on a registered post box to claim it. +- Admin/mods can look at a sign and use '/postoffice claim playername' to claim a post box on behalf of another player. - Enjoy your awesome post office on your SMP! ![Plugin Usage Stats](https://bstats.org/signatures/bukkit/Post%20Office.svg) +## Commands +Below are the commands used to manage the post office barrels and plugin: + +### /postoffice register +Place a barrel and a sign on the front. Run this command while looking at the sign to register the post box in the config. + +### /postoffice claim +If players have the claim permission, this command will allow them to claim an already registered post box. + +### /postoffice claim playername +Used for an admin/mod to claim a post box on behalf of another player. + +### /postoffice remove +Run while looking at a registered or claimed post box to remove the owner and the post box from the config. + +### /postoffice info +Run while looking at a post box barrel to get information about the registration state/owner. + + ## Permissions The below permissions are intended for giving your mods extra access and abilities within your post office. By default, players will be able to access their own post box without any additional permission nodes being granted. +### shantek.postoffice.use +This permission prevents a player from using/interacting with the post office. All players have this by default, so use this to deny access to any players you wish to ban from the post box system. + ### shantek.postoffice.removeitems -Allow these players to remove items from any post box +Allow these players to remove items from any post box. + +### shantek.postoffice.register +Allow a player to register/remove a post box in the config. -### shantek.postoffice.create -Allow a player to create a post box +### shantek.postoffice.claim +Allow a player to claim their own post box. -### shantek.postoffice.break -Allow a player to break any post box +### shantek.postoffice.claim.others +Allow a player to claim a post box for other players (generally used by admin/mods). ### shantek.postoffice.updatenotification -Any player with this permission will be notified if there is an update to the plugin +Any player with this permission will be notified if there is an update to the plugin. ## External Links diff --git a/src/main/java/io/shantek/PostOffice.java b/src/main/java/io/shantek/PostOffice.java index 2302812..4456fbe 100644 --- a/src/main/java/io/shantek/PostOffice.java +++ b/src/main/java/io/shantek/PostOffice.java @@ -1,18 +1,13 @@ package io.shantek; +import io.shantek.functions.*; +import io.shantek.listeners.*; + import java.io.File; import java.io.IOException; import java.util.logging.*; import java.util.stream.Collectors; - -import io.shantek.functions.*; -import io.shantek.listeners.*; import org.bukkit.Bukkit; -import org.bukkit.command.PluginCommand; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.permissions.Permission; -import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import java.nio.file.*; import java.util.*; @@ -28,9 +23,7 @@ public final class PostOffice extends JavaPlugin { public Helpers helpers; public BarrelProtection barrelProtection; public TabCompleter tabCompleter; - public static PostOffice instance; - public String customBarrelName = "pobox"; public File mailFile; public int previousItemCount = 0; @@ -40,6 +33,7 @@ public final class PostOffice extends JavaPlugin { public boolean postBoxProtection = true; public boolean consoleLogs = true; public boolean gotMailDelay = true; + public boolean signNotification = true; public void onEnable() { @@ -50,22 +44,20 @@ public void onEnable() { helpers = new Helpers(this); TabCompleter tabCompleter = new TabCompleter(this); - getCommand("postoffice").setTabCompleter(new TabCompleter(this)); + Objects.requireNonNull(getCommand("postoffice")).setTabCompleter(new TabCompleter(this)); // Check for a data folder, create it if needed helpers.checkForDataFolder(); - this.mailFile = new File(getDataFolder(), "mail.txt"); + this.mailFile = new File(getDataFolder(), "hasmail.txt"); - getCommand("postoffice").setExecutor(new Commands(this)); + Objects.requireNonNull(getCommand("postoffice")).setExecutor(new Commands(this)); // Create an instance of UpdateChecker this.updateChecker = new UpdateChecker(); // Create an instance of PluginConfig this.pluginConfig = new PluginConfig(this); - - registerPluginPermissions(); pluginConfig.reloadConfigFile(); if (this.mailFile.exists()) { @@ -112,32 +104,17 @@ public static PostOffice getInstance() { if (instance == null) { instance = new PostOffice(); } - return instance; } - public void printInfoMessage(String message) { - getLogger().info(message); // Print to the console + @Override + public void onDisable() { + // Save the cache to file when the plugin is disabled + helpers.saveCacheToFile(); } - private void registerPluginPermissions() { - // Register the permission node - Permission removeItemsPermission = new Permission("shantek.postoffice.removeitems"); - PluginManager pm = getServer().getPluginManager(); - pm.addPermission(removeItemsPermission); - - // Permission for breaking Post Boxes - Permission breakPermission = new Permission("shantek.postoffice.break"); - pm.addPermission(breakPermission); - - // Permission for creating Post Boxes - Permission createBoxPermission = new Permission("shantek.postoffice.create"); - pm.addPermission(createBoxPermission); - - // Permission for breaking Post Boxes - Permission updateNotificationPermission = new Permission("shantek.postoffice.updatenotification"); - pm.addPermission(updateNotificationPermission); + public void printInfoMessage(String message) { + getLogger().info(message); // Print to the console } - } \ No newline at end of file diff --git a/src/main/java/io/shantek/functions/BarrelData.java b/src/main/java/io/shantek/functions/BarrelData.java new file mode 100644 index 0000000..af0aaaa --- /dev/null +++ b/src/main/java/io/shantek/functions/BarrelData.java @@ -0,0 +1,27 @@ +package io.shantek.functions; + +import java.util.UUID; + +public class BarrelData { + private final UUID ownerUUID; + private final String signLocation; + private final String state; + + public BarrelData(UUID ownerUUID, String signLocation, String state) { + this.ownerUUID = ownerUUID; + this.signLocation = signLocation; + this.state = state; + } + + public UUID getOwnerUUID() { + return ownerUUID; + } + + public String getSignLocation() { + return signLocation; + } + + public String getState() { + return state; + } +} diff --git a/src/main/java/io/shantek/functions/Commands.java b/src/main/java/io/shantek/functions/Commands.java index 0fe4a70..d953b43 100644 --- a/src/main/java/io/shantek/functions/Commands.java +++ b/src/main/java/io/shantek/functions/Commands.java @@ -1,13 +1,17 @@ package io.shantek.functions; import io.shantek.PostOffice; -import org.bukkit.ChatColor; +import org.bukkit.*; +import org.bukkit.block.Barrel; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; +import java.util.UUID; public class Commands implements CommandExecutor { @@ -20,11 +24,73 @@ public Commands(PostOffice postOffice) { @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if (cmd.getName().equalsIgnoreCase("postoffice")) { - if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { + + // Postoffice remove command + if (args[0].equalsIgnoreCase("remove")) { + if (sender instanceof Player) { + Player player = (Player) sender; + + // Ensure they have the proper permission + if (player.hasPermission("shantek.postoffice.register") || player.isOp()) { + + // Get the block the player is looking at (sign or barrel) + Block targetBlock = player.getTargetBlock(null, 10); + + Block barrelBlock = null; + + // Check if the player is looking at a sign + if (Tag.SIGNS.isTagged(targetBlock.getType())) { + // Retrieve the attached barrel + barrelBlock = postOffice.helpers.getAttachedBarrel(targetBlock); + } else if (targetBlock.getType() == Material.BARREL) { + // Player is looking directly at a barrel + barrelBlock = targetBlock; + } + + // Ensure we have a valid barrel block + if (barrelBlock == null || barrelBlock.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.lookAtPostBox)); + return true; + } + + // Check if the barrel exists in the config (registered post box) + if (!postOffice.helpers.isBarrelInConfig(barrelBlock)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.notRegistered)); + return true; + } + + // Optionally clear the sign associated with the post box + Block signBlock = postOffice.helpers.getSignForBarrel(barrelBlock); + if (signBlock != null && signBlock.getState() instanceof Sign) { + Sign sign = (Sign) signBlock.getState(); + sign.setLine(1, ""); // Clear the second line + sign.setLine(2, ""); // Clear the third line + sign.update(); // Update the sign + } + + // Call the helper to remove the barrel from the cache and config + postOffice.helpers.removeBarrelFromCache(barrelBlock); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.postBoxRemoved)); + + return true; + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.noPermission)); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "This command can only be used by players."); + return true; + } + } + + else if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { if (sender.hasPermission("shantek.postoffice.reload") || sender.isOp()) { // Reload all the config and language file postOffice.pluginConfig.reloadConfigFile(); + postOffice.helpers.saveCacheToFile(); // Save cache instead of config directly + postOffice.helpers.reloadBarrelsConfig(); sender.sendMessage(ChatColor.GREEN + "Post Office config file has been reloaded."); return true; } else { @@ -32,6 +98,286 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String sender.sendMessage(ChatColor.RED + "You don't have access to this command!"); return false; } + } else if (args[0].equalsIgnoreCase("info")) { + if (sender instanceof Player) { + Player player = (Player) sender; + + Block targetBlock = player.getTargetBlock(null, 10); // Get the block the player is looking at + + Block barrelBlock = null; + + // Check if the player is looking at a sign + if (Tag.SIGNS.isTagged(targetBlock.getType())) { + // Look up the barrel using the sign's location from the config + barrelBlock = postOffice.helpers.getBarrelFromSign(targetBlock); + + // If there's no attached barrel or it's not a valid post box, show unregistered + if (barrelBlock == null || barrelBlock.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.notRegistered)); + return true; + } + + } else if (targetBlock.getType() == Material.BARREL) { + // The player is directly looking at a barrel + barrelBlock = targetBlock; + } + + // Ensure we have a valid barrel block + if (barrelBlock == null || barrelBlock.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.notRegistered)); + return true; + } + + // Use the helper to get the owner and state information + String barrelLocation = postOffice.helpers.getBlockLocationString(barrelBlock); + String owner = postOffice.helpers.getOwnerNameFromConfig(barrelLocation); // Get the owner name + String state = postOffice.helpers.getStateFromConfig(barrelLocation); // Get the post box state + + // If there's an owner, print it. Otherwise, print the state. + if (owner != null && !owner.equals("none")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.postBoxOwner + .replace("%owner%", owner) + )); + } else if (state != null && state.equals("registered")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.unclaimedPostbox)); + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.invalidPostbox)); + } + + return true; + } + } else if (args[0].equalsIgnoreCase("register")) { + + if (sender instanceof Player) { + Player player = (Player) sender; + if (sender.hasPermission("shantek.postoffice.register") || sender.isOp()) { + + // Ensure they are looking at a sign + Block targetBlock = player.getTargetBlock(null, 10); // Max distance 10 blocks + if (targetBlock == null || !(targetBlock.getState() instanceof Sign)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.lookAtPostBox)); + return true; + } + + // Ensure the sign is attached to a barrel + Block attachedBarrel = postOffice.helpers.getAttachedBarrel(targetBlock); + if (attachedBarrel == null || attachedBarrel.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.signOnBarrel)); + return true; + } + + // Get the location of the barrel and check its state + String barrelLocation = postOffice.helpers.getBlockLocationString(attachedBarrel); + String currentOwner = postOffice.helpers.getOwnerNameFromConfig(barrelLocation); // Get the owner name + String currentState = postOffice.helpers.getStateFromConfig(barrelLocation); // Get the post box state + + // Check if the post box is already registered or claimed + if (currentOwner != null && !currentOwner.equals("none")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.postBoxOwner + .replace("%owner%", currentOwner) + )); + return true; + } + if (currentState != null && currentState.equals("registered")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.alreadyRegistered)); + return true; + } + + // Register the post box and update the barrel name + Barrel barrel = (Barrel) attachedBarrel.getState(); + barrel.setCustomName(postOffice.customBarrelName); + barrel.update(); + + // Register the barrel and sign in the plugin config + UUID barrelOwnerUUID = null; // No owner yet + postOffice.helpers.addOrUpdateBarrelInCache(attachedBarrel, targetBlock, barrelOwnerUUID, "registered"); + + // Update the sign with "Unclaimed" on the second line in red text + Sign sign = (Sign) targetBlock.getState(); + sign.setLine(1, ChatColor.RED + "Unclaimed"); + sign.update(); // Make sure to update the sign to apply the changes + + postOffice.helpers.saveCacheToFile(); // Save the cache to disk + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.successfulRegistration)); + return true; + + } else { + // Player doesn't have permission to use the command + sender.sendMessage(ChatColor.RED + "You don't have access to this command!"); + return false; + + } + } else { + sender.sendMessage(ChatColor.RED + "This command can only be used by players."); + return true; + } + } else if (args[0].equalsIgnoreCase("claim") && args.length == 2) { + if (sender.hasPermission("shantek.postoffice.claim.others") || sender.isOp()) { + + // Ensure they are looking at a sign + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "This command can only be used by players."); + return true; + } + + Player player = (Player) sender; + Block targetBlock = player.getTargetBlock(null, 10); // Max distance 10 blocks + if (targetBlock == null || !(targetBlock.getState() instanceof Sign)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.lookAtPostBox)); + return true; + } + + // Ensure the sign is attached to a barrel + Block attachedBarrel = postOffice.helpers.getAttachedBarrel(targetBlock); + if (attachedBarrel == null || attachedBarrel.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.signOnBarrel)); + return true; + } + + // Check if the sign exists in the config (if the post box has been registered) + Block savedSign = postOffice.helpers.getSignForBarrel(attachedBarrel); // Retrieve saved sign + if (savedSign == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.signOnBarrel)); + return true; + } + + // Get the location of the barrel + String barrelLocation = postOffice.helpers.getBlockLocationString(attachedBarrel); + + // Get the target player's name and UUID + String targetPlayerName = args[1]; + OfflinePlayer targetPlayer = Bukkit.getOfflinePlayer(targetPlayerName); + + if (!targetPlayer.hasPlayedBefore()) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.notPlayedBefore + .replace("%player%", targetPlayer.toString()) + )); + return true; + } + + UUID targetPlayerUUID = targetPlayer.getUniqueId(); + + // Check if the player already has a post box + if (postOffice.helpers.doesPlayerHavePostBox(targetPlayerUUID)) { + String existingPostBoxLocation = postOffice.helpers.getPlayerPostBoxLocation(targetPlayerUUID); // Get world and coordinates + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.alreadyHasPostBox + .replace("%player%", targetPlayerName) + .replace("%location%", existingPostBoxLocation))); + return true; + } + + // Check if the post box is already claimed + String currentOwner = postOffice.helpers.getOwnerNameFromConfig(barrelLocation); + if (currentOwner != null && !currentOwner.equals("none")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.alreadyClaimed)); + return true; + } + + // Claim the post box for the target player and update the state to 'claimed' + postOffice.helpers.addOrUpdateBarrelInCache(attachedBarrel, targetBlock, targetPlayerUUID, "claimed"); + + // Update the sign to display the player's name on the second line + if (targetBlock != null && targetBlock.getState() instanceof Sign) { + Sign sign = (Sign) targetBlock.getState(); + sign.setLine(1, targetPlayer.getName()); // Set the player's name on the 2nd line + sign.update(); + } + + // Confirm to the person running the command that it worked + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.claimedFor + .replace("%owner%", targetPlayerName))); + + // Let the owner know they now have a post box, if they're online + if (targetPlayer.isOnline()) { + Objects.requireNonNull(targetPlayer.getPlayer()).sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.claimedForOtherPlayer)); + } + postOffice.helpers.saveCacheToFile(); // Save the cache to disk + return true; + } + else { + // Player doesn't have permission to use the command + sender.sendMessage(ChatColor.RED + "You don't have access to this command!"); + return false; + } + } else if (args[0].equalsIgnoreCase("claim")) { + if (sender instanceof Player) { + Player player = (Player) sender; + UUID playerUUID = player.getUniqueId(); + + if (sender.hasPermission("shantek.postoffice.claim") || sender.isOp()) { + + // Ensure they are looking at a sign + Block targetBlock = player.getTargetBlock(null, 10); // Max distance 10 blocks + if (targetBlock == null || !(targetBlock.getState() instanceof Sign)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.lookAtPostBox)); + return true; + } + + // Ensure the sign is attached to a barrel + Block attachedBarrel = postOffice.helpers.getAttachedBarrel(targetBlock); + if (attachedBarrel == null || attachedBarrel.getType() != Material.BARREL) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.signOnBarrel)); + return true; + } + + // Check if the sign exists in the config (if the post box has been registered) + Block savedSign = postOffice.helpers.getSignForBarrel(attachedBarrel); // Retrieve saved sign + if (savedSign == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.notRegistered)); + return true; + } + + // Get the location of the barrel + String barrelLocation = postOffice.helpers.getBlockLocationString(attachedBarrel); + + // Check if the post box is already claimed + String currentOwner = postOffice.helpers.getOwnerNameFromConfig(barrelLocation); + if (currentOwner != null && !currentOwner.equals("none")) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.alreadyClaimed)); + return true; + } + + // Check if the player already has a post box + if (postOffice.helpers.doesPlayerHavePostBox(playerUUID)) { + String existingPostBoxLocation = postOffice.helpers.getPlayerPostBoxLocation(playerUUID); // Get world and coordinates + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.alreadyHasPostBox + .replace("%player%", player.getName()) + .replace("%location%", existingPostBoxLocation))); + + return true; + } + + // Claim the post box for the player and update the state to 'claimed' + postOffice.helpers.addOrUpdateBarrelInCache(attachedBarrel, targetBlock, playerUUID, "claimed"); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.successfullyClaimed)); + + // Update the sign to display the player's name on the second line + if (targetBlock != null && targetBlock.getState() instanceof Sign) { + Sign sign = (Sign) targetBlock.getState(); + sign.setLine(1, player.getName()); // Set the player's name on the 2nd line + sign.update(); + } + + postOffice.helpers.saveCacheToFile(); // Save the cache to disk + return true; + } else { + // Player doesn't have permission to use the command + sender.sendMessage(ChatColor.RED + "You don't have access to this command!"); + return false; + } + } else { + sender.sendMessage(ChatColor.RED + "This command can only be used by players."); + return true; + } } else { // Invalid command format sender.sendMessage(ChatColor.RED + "Unknown command or insufficient permission."); @@ -40,6 +386,4 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } return false; } - } - diff --git a/src/main/java/io/shantek/functions/Helpers.java b/src/main/java/io/shantek/functions/Helpers.java index ac6d1d1..8366991 100644 --- a/src/main/java/io/shantek/functions/Helpers.java +++ b/src/main/java/io/shantek/functions/Helpers.java @@ -1,34 +1,99 @@ package io.shantek.functions; import io.shantek.PostOffice; -import org.bukkit.Material; -import org.bukkit.Tag; -import org.bukkit.block.Barrel; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.inventory.ItemStack; +import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.*; import java.util.logging.Level; +import org.bukkit.*; +import org.bukkit.block.*; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + public class Helpers { private final PostOffice postOffice; public Helpers(PostOffice postOffice) { this.postOffice = postOffice; + barrelsCache = new HashMap<>(); + loadBarrelsIntoCache(); // Load data from barrels.yml into the cache at startup } - public void saveMailFile() { - try { - if (postOffice.consoleLogs) { - postOffice.getLogger().info("The mail list has been updated."); + public Map barrelsCache; + private FileConfiguration barrelsConfig = null; + private File barrelsConfigFile = null; + + //region Post box management + + // Helper method to get block location string + public String getBlockLocationString(Block block) { + return block.getWorld().getName() + "_" + block.getX() + "_" + block.getY() + "_" + block.getZ(); + } + + // Get the owner name from the barrels.yml config + public String getOwnerNameFromConfig(String barrelLocationString) { + FileConfiguration barrelsConfig = getBarrelsConfig(); + + String ownerUUIDString = barrelsConfig.getString("barrels." + barrelLocationString + ".owner"); + + if (ownerUUIDString != null && !ownerUUIDString.equals("none")) { + UUID ownerUUID = UUID.fromString(ownerUUIDString); + return getPlayerName(ownerUUID); // Retrieve player's name from UUID + } + + return "none"; // No owner + } + + public boolean doesPlayerHavePostBox(UUID playerUUID) { + FileConfiguration barrelsConfig = getBarrelsConfig(); + + // Check if the 'barrels' section exists before accessing it + if (barrelsConfig.contains("barrels")) { + ConfigurationSection barrelsSection = barrelsConfig.getConfigurationSection("barrels"); + + // Loop through all barrels to check if any are owned by this player + assert barrelsSection != null; + for (String barrelLocation : barrelsSection.getKeys(false)) { + String ownerUUIDString = barrelsConfig.getString("barrels." + barrelLocation + ".owner"); + if (ownerUUIDString != null && ownerUUIDString.equals(playerUUID.toString())) { + return true; // The player already owns a post box + } } - Files.write(postOffice.mailFile.toPath(), postOffice.playersWithMail); - } catch (IOException e) { - postOffice.getLogger().log(Level.SEVERE, "Error updating the mail file.", e); } + return false; // The player does not have a post box + } + + public String getPlayerPostBoxLocation(UUID playerUUID) { + FileConfiguration barrelsConfig = getBarrelsConfig(); + + // Loop through all barrels to find the one owned by the player + for (String barrelLocation : Objects.requireNonNull(barrelsConfig.getConfigurationSection("barrels")).getKeys(false)) { + String ownerUUIDString = barrelsConfig.getString("barrels." + barrelLocation + ".owner"); + if (ownerUUIDString != null && ownerUUIDString.equals(playerUUID.toString())) { + + // Check if world, x, y, z are stored properly + String worldName = barrelsConfig.getString("barrels." + barrelLocation + ".world", "Unknown World"); + int x = barrelsConfig.getInt("barrels." + barrelLocation + ".x", Integer.MIN_VALUE); + int y = barrelsConfig.getInt("barrels." + barrelLocation + ".y", Integer.MIN_VALUE); + int z = barrelsConfig.getInt("barrels." + barrelLocation + ".z", Integer.MIN_VALUE); + + // Check if coordinates are valid (i.e., not the fallback value) + if (x == Integer.MIN_VALUE || y == Integer.MIN_VALUE || z == Integer.MIN_VALUE) { + return "Unknown location"; // Coordinates are not valid + } + + // Return formatted location string + return worldName + " [" + x + ", " + y + ", " + z + "]"; + } + } + return "Unknown location"; // Fallback if no post box is found } public int countNonNullItems(ItemStack[] items) { @@ -41,16 +106,146 @@ public int countNonNullItems(ItemStack[] items) { return count; } - public void checkForDataFolder() { - if (!postOffice.getDataFolder().exists()) { - if (postOffice.getDataFolder().mkdir()) { - postOffice.getLogger().info("Data folder created successfully."); + public OfflinePlayer getPlayer(UUID uuid) { + return Bukkit.getOfflinePlayer(uuid); + } + + public boolean isPostBoxOwner(Block block, Player player) { + UUID playerUUID = player.getUniqueId(); + return getOwnerUUID(block).equals(playerUUID); + + } + + public UUID getOwnerUUID(Block block) { + String blockLocationString = getBlockLocationString(block); + BarrelData barrelData = barrelsCache.get(blockLocationString); + return barrelData != null ? barrelData.getOwnerUUID() : null; + } + + // Method to get a player's name by UUID, checking both online and offline players + public String getPlayerName(UUID uuid) { + // Check if the player is online + Player onlinePlayer = Bukkit.getPlayer(uuid); + if (onlinePlayer != null) { + return onlinePlayer.getName(); + } + + // If the player is offline, use getOfflinePlayer + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + return offlinePlayer.getName(); // This may return null if the player's name is not available + } + + public Block getSignForBarrel(Block barrelBlock) { + String barrelLocationString = getBlockLocationString(barrelBlock); + + // Retrieve the sign's location from the config + String signLocationString = barrelsConfig.getString("barrels." + barrelLocationString + ".sign"); + + if (signLocationString != null) { + String[] parts = signLocationString.split("_"); + + // Ensure the parts are valid (should be 4 parts: world, x, y, z) + if (parts.length == 4) { + String worldName = parts[0]; + int x, y, z; + + try { + x = Integer.parseInt(parts[1]); + y = Integer.parseInt(parts[2]); + z = Integer.parseInt(parts[3]); + } catch (NumberFormatException e) { + postOffice.getLogger().warning("Invalid sign coordinates in config for barrel: " + barrelLocationString); + return null; // Invalid coordinates, return null + } + + World world = Bukkit.getWorld(worldName); + if (world != null) { + return world.getBlockAt(x, y, z); // Return the block at the saved sign location + } else { + postOffice.getLogger().warning("World not found for barrel: " + barrelLocationString); + } } else { - postOffice.getLogger().warning("Error creating the data folder."); + postOffice.getLogger().warning("Invalid sign location string format for barrel: " + barrelLocationString); + } + } + + return null; // Sign not found or invalid format + } + + public Block getAttachedBarrel(Block signBlock) { + for (BlockFace face : BlockFace.values()) { + Block attachedBlock = signBlock.getRelative(face); + if (attachedBlock.getType() == Material.BARREL) { + return attachedBlock; + } + } + return null; // No barrel found attached to the sign + } + + public Block getBarrelFromSign(Block signBlock) { + String signLocationString = getBlockLocationString(signBlock); + FileConfiguration barrelsConfig = getBarrelsConfig(); + + // Ensure we have a valid configuration section for barrels + ConfigurationSection section = barrelsConfig.getConfigurationSection("barrels"); + if (section == null) { + postOffice.getLogger().severe("Barrels section is missing in the config"); + return null; + } + + // Loop through all stored barrels to find one with this sign location + for (String barrelLocation : section.getKeys(false)) { + String storedSignLocation = barrelsConfig.getString("barrels." + barrelLocation + ".sign"); + + if (signLocationString.equals(storedSignLocation)) { + // The sign matches, get the barrel block + String[] parts = barrelLocation.split("_"); + if (parts.length == 4) { + String worldName = parts[0]; + int x = Integer.parseInt(parts[1]); + int y = Integer.parseInt(parts[2]); + int z = Integer.parseInt(parts[3]); + + World world = Bukkit.getWorld(worldName); + if (world != null) { + return world.getBlockAt(x, y, z); // Return the barrel block + } + } + } + } + return null; // No barrel found for this sign + } + + // Check if there's a barrel with the custom name nearby + public boolean hasBarrelNearby(Block block) { + // Check if the given block is a barrel with the custom name + if (block.getType() == Material.BARREL) { + Barrel barrel = (Barrel) block.getState(); + String barrelCustomName = barrel.getCustomName(); + + if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { + return true; } } + + // Check if any nearby block is a barrel with the custom name + for (BlockFace blockFace : BlockFace.values()) { + Block relativeBlock = block.getRelative(blockFace); + + if (relativeBlock.getType() == Material.BARREL) { + Barrel barrel = (Barrel) relativeBlock.getState(); + String barrelCustomName = barrel.getCustomName(); + + if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { + return true; + } + } + } + + return false; } + // Check if the block is a protected post box public boolean isProtectedPostBox(Block block) { if (block.getType() == Material.BARREL) { Barrel barrel = (Barrel) block.getState(); @@ -62,6 +257,7 @@ public boolean isProtectedPostBox(Block block) { return false; } + // Check if a sign is next to a protected barrel public boolean isSignNextToProtectedBarrel(Block signBlock) { BlockFace[] adjacentFaces = { BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN @@ -79,4 +275,236 @@ public boolean isSignNextToProtectedBarrel(Block signBlock) { } return false; } + + //endregion + + //region Configuration loading/saving + + public void removeBarrelFromCache(Block barrelBlock) { + String barrelLocationString = getBlockLocationString(barrelBlock); + + // Retrieve the associated sign before removing the barrel from the cache + BarrelData barrelData = barrelsCache.get(barrelLocationString); + if (barrelData != null) { + String signLocationString = barrelData.getSignLocation(); + if (signLocationString != null) { + // Get the sign block from its location string + String[] parts = signLocationString.split("_"); + if (parts.length == 4) { + World world = Bukkit.getWorld(parts[0]); + int x = Integer.parseInt(parts[1]); + int y = Integer.parseInt(parts[2]); + int z = Integer.parseInt(parts[3]); + + if (world != null) { + Block signBlock = world.getBlockAt(x, y, z); + if (signBlock.getState() instanceof Sign) { + // Clear the sign text (e.g., remove the player's name and "Unclaimed" message) + Sign sign = (Sign) signBlock.getState(); + sign.setLine(0, ""); // Clear the first line + sign.setLine(1, ""); // Clear the second line + sign.setLine(2, ""); // Clear the third line + sign.setLine(3, ""); // Clear the fourth line + sign.update(); // Update the sign + } + } + } + } + } + + // Remove the barrel from the cache + barrelsCache.remove(barrelLocationString); + + // Save the cache to file immediately + saveCacheToFile(); + } + + // Get the state of the post box from the barrels.yml config + public String getStateFromConfig(String barrelLocationString) { + FileConfiguration barrelsConfig = getBarrelsConfig(); + return barrelsConfig.getString("barrels." + barrelLocationString + ".state", "unregistered"); // Default to "registered" + } + + public boolean isBarrelInConfig(Block block) { + String blockLocationString = getBlockLocationString(block); + return barrelsCache.containsKey(blockLocationString); + } + + private void loadBarrelsIntoCache() { + barrelsCache.clear(); // Clear the cache before reloading + + FileConfiguration barrelsConfig = getBarrelsConfig(); + if (barrelsConfig.contains("barrels")) { + Set keys = Objects.requireNonNull(barrelsConfig.getConfigurationSection("barrels")).getKeys(false); + //postOffice.getLogger().info("Loading barrels into cache. Found keys: " + keys); + + for (String key : keys) { + String path = "barrels." + key; + String ownerUUIDString = barrelsConfig.getString(path + ".owner"); + String state = barrelsConfig.getString(path + ".state", "unregistered"); // Default to "unregistered" + String signLocation = barrelsConfig.getString(path + ".sign"); + + UUID ownerUUID = null; + if (ownerUUIDString != null && !ownerUUIDString.equalsIgnoreCase("none")) { + try { + ownerUUID = UUID.fromString(ownerUUIDString); + } catch (IllegalArgumentException e) { + postOffice.getLogger().warning("Invalid UUID found for barrel at " + key); + continue; // Skip this barrel if UUID is invalid + } + } + + // Create a BarrelData object and store it in the cache + BarrelData barrelData = new BarrelData(ownerUUID, state, signLocation); + barrelsCache.put(key, barrelData); // Add to cache + + /* + postOffice.getLogger().info("Added barrel to cache at location: " + key + ", Owner: " + + (ownerUUID != null ? ownerUUID : "none") + ", State: " + state + ", Sign: " + signLocation); + + */ + } + } else { + postOffice.getLogger().warning("No barrels found in barrels.yml during cache load."); + } + } + + public void addOrUpdateBarrelInCache(Block barrelBlock, Block signBlock, UUID ownerUUID, String state) { + String barrelLocationString = getBlockLocationString(barrelBlock); + String signLocationString = getBlockLocationString(signBlock); + + // Create the BarrelData object with correct sign location and state + BarrelData barrelData = new BarrelData(ownerUUID, signLocationString, state); + + // Add or update the barrel data in the cache + barrelsCache.put(barrelLocationString, barrelData); + + // Log for debugging purposes + postOffice.getLogger().info("Adding/Updating barrel at: " + barrelLocationString); + postOffice.getLogger().info("Sign location for barrel: " + signLocationString); + postOffice.getLogger().info("Post box state: " + state); + + // Optionally save the cache to disk immediately + saveCacheToFile(); + } + + public void saveCacheToFile() { + FileConfiguration barrelsConfig = getBarrelsConfig(); + + // Clear the existing barrels section in the config + barrelsConfig.set("barrels", null); + + // Iterate over the cache and save each barrel to the config + for (Map.Entry entry : barrelsCache.entrySet()) { + String barrelLocationString = entry.getKey(); + BarrelData barrelData = entry.getValue(); + + String path = "barrels." + barrelLocationString; + barrelsConfig.set(path + ".owner", barrelData.getOwnerUUID() != null ? barrelData.getOwnerUUID().toString() : "none"); + barrelsConfig.set(path + ".sign", barrelData.getSignLocation()); + barrelsConfig.set(path + ".state", barrelData.getState()); + + // Parse location from the key (world, x, y, z) + String[] parts = barrelLocationString.split("_"); + if (parts.length == 4) { + barrelsConfig.set(path + ".world", parts[0]); + barrelsConfig.set(path + ".x", Integer.parseInt(parts[1])); + barrelsConfig.set(path + ".y", Integer.parseInt(parts[2])); + barrelsConfig.set(path + ".z", Integer.parseInt(parts[3])); + } + } + + // Save the config to disk + saveBarrelsConfig(); + } + + public void checkForDataFolder() { + if (!postOffice.getDataFolder().exists()) { + if (postOffice.getDataFolder().mkdir()) { + postOffice.getLogger().info("Data folder created successfully."); + } else { + postOffice.getLogger().warning("Error creating the data folder."); + } + } + } + + public void saveMailFile() { + try { + if (postOffice.consoleLogs) { + postOffice.getLogger().info("The mail list has been updated."); + } + Files.write(postOffice.mailFile.toPath(), postOffice.playersWithMail); + } catch (IOException e) { + postOffice.getLogger().log(Level.SEVERE, "Error updating the mail file.", e); + } + } + + // Load the barrels.yml configuration + public void reloadBarrelsConfig() { + if (barrelsConfigFile == null) { + barrelsConfigFile = new File(postOffice.getDataFolder(), "barrels.yml"); + } + barrelsConfig = YamlConfiguration.loadConfiguration(barrelsConfigFile); + + // Create the file if it doesn't exist + if (!barrelsConfigFile.exists()) { + postOffice.saveResource("barrels.yml", false); + } + + // Debugging to check if it's loading correctly + if (barrelsConfig.contains("barrels")) { + Set keys = Objects.requireNonNull(barrelsConfig.getConfigurationSection("barrels")).getKeys(false); + postOffice.getLogger().info("Keys in barrels.yml after reload: " + keys.toString()); + } else { + postOffice.getLogger().warning("No barrels found in barrels.yml during reload."); + } + } + + // Get the barrels.yml configuration + public FileConfiguration getBarrelsConfig() { + if (barrelsConfig == null) { + reloadBarrelsConfig(); + } + return barrelsConfig; + } + + // Save changes to barrels.yml + public void saveBarrelsConfig() { + if (barrelsConfig == null || barrelsConfigFile == null) { + return; + } + try { + barrelsConfig.save(barrelsConfigFile); + } catch (IOException e) { + postOffice.getLogger().severe("Could not save barrels.yml: " + e.getMessage()); + } + } + + public Block getSignFromConfig(Block barrelBlock) { + String barrelLocationString = getBlockLocationString(barrelBlock); // Convert block to location string + String path = "barrels." + barrelLocationString + ".sign"; // Use this location string in the config + + // Look up the sign location in the config + if (barrelsConfig.contains(path)) { + String signLocation = barrelsConfig.getString(path); + assert signLocation != null; + String[] parts = signLocation.split("_"); + + if (parts.length == 4) { + String worldName = parts[0]; + int x = Integer.parseInt(parts[1]); + int y = Integer.parseInt(parts[2]); + int z = Integer.parseInt(parts[3]); + + World world = Bukkit.getWorld(worldName); + if (world != null) { + return world.getBlockAt(x, y, z); // Return the block at the saved sign location + } + } + } + return null; // Sign not found in the config + } + + //endregion + } diff --git a/src/main/java/io/shantek/functions/Language.java b/src/main/java/io/shantek/functions/Language.java index a97f6b4..23dc57c 100644 --- a/src/main/java/io/shantek/functions/Language.java +++ b/src/main/java/io/shantek/functions/Language.java @@ -12,15 +12,29 @@ public Language(PostOffice postOffice) { public String sentMessage = "&a[Post Office] &aMail sent to %receiver%."; public String receivedMessage = "&a[Post Office] &eYou received mail from %sender%!"; public String gotMailMessage = "&a[Post Office] &fYou got mail!"; - public String cantStackItems = "&a[Post Office] &4You don't have permission to do that."; - public String removeItemError = "&a[Post Office] &4You don't have permission to remove items."; - public String offHandError = "&a[Post Office] &4No offhand usage while in a Post Box!"; - public String hotBarError = "&a[Post Office] &4No hot bar usage while in a Post Box!"; - public String breakError = "&a[Post Office] &4You can't break a Post Box."; - public String createError = "&a[Post Office] &4You can't create a Post Box."; + public String noPermission = "&a[Post Office] &4You don't have permission to do that."; + public String denyAction = "&a[Post Office] &4You can't do that here!"; + public String notRegistered = "&a[Post Office] &4This isn't a registered post office box."; + public String postBoxRemoved = "&a[Post Office] &aPost box removed successfully."; + public String successfulRegistration = "&a[Post Office] &aPost box registered successfully."; + public String alreadyRegistered = "&a[Post Office] &4This post box is already registered."; public String postboxCreated = "&a[Post Office] &4 Box successfully created for %username%"; + public String removeFromConfig = "&a[Post Office] &aPost box successfully removed from the config."; + public String lookAtPostBox = "&a[Post Office] &4You must be looking at a barrel or a sign attached to a barrel."; + public String signOnBarrel = "&a[Post Office] &4The sign must be attached to a barrel."; + public String alreadyClaimed = "&a[Post Office] &4This post box has already been claimed."; + public String invalidPostbox = "&a[Post Office] &4This isn't a valid post box."; + public String successfullyClaimed = "&a[Post Office] &aYou have successfully registered this post box."; + public String modifySign = "&a[Post Office] &4You cannot modify a post box sign."; + public String unclaimedPostbox = "&a[Post Office] &4This post box is unclaimed."; + public String userBanned = "&a[Post Office] &4You aren't able to interact with this post box."; + public String postBoxOwner = "&a[Post Office] &aThis post box is owned by %owner%"; + public String claimedFor = "&a[Post Office] &aThis post box has been claimed for %owner%"; + public String alreadyHasPostBox = "&a[Post Office] &4%player% already has a post box at: %location%"; + public String notPlayedBefore = "&a[Post Office] &4The player %player% has not played on this server."; + public String claimedForOtherPlayer = "&a[Post Office] &aA post box has been created for you."; public String pluginUpToDate = "Your plugin is up-to-date."; - public String dropItemError = "&a[Post Office] &4 You can't drop items while in a postbox."; + public UpdateChecker updateChecker; public PluginConfig pluginConfig; diff --git a/src/main/java/io/shantek/functions/PluginConfig.java b/src/main/java/io/shantek/functions/PluginConfig.java index 024cb43..44129bc 100644 --- a/src/main/java/io/shantek/functions/PluginConfig.java +++ b/src/main/java/io/shantek/functions/PluginConfig.java @@ -1,17 +1,17 @@ package io.shantek.functions; +import io.shantek.PostOffice; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.HashMap; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.*; import java.util.logging.Level; -import io.shantek.PostOffice; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import java.nio.file.*; -import java.util.*; -@SuppressWarnings("SameParameterValue") public class PluginConfig { private final PostOffice postOffice; @@ -20,213 +20,164 @@ public PluginConfig(PostOffice postOffice) { this.postOffice = postOffice; } - public void reloadConfigFile() { - try { - postOffice.getLogger().info("Reloading config file."); // Print to the console - - File configFile = new File(postOffice.getDataFolder(), "config.yml"); - - // Check if the config file exists - if (!configFile.exists()) { - postOffice.getLogger().info("Config file not found. Creating a new one..."); - - // Create a new config file based on a template from resources - saveDefaultConfig("config.yml", configFile); - } else { - FileConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Check for missing keys - @SuppressWarnings("unused") boolean keysMissing = checkForMissingKeys(config); + private FileConfiguration barrelsConfig = null; + private File barrelsConfigFile = null; - // Save existing values of missing keys - Map missingKeyValues = saveMissingKeyValues(config); - - // Create a fresh config file - saveDefaultConfig("config.yml", configFile); - - // Load the new config - config = YamlConfiguration.loadConfiguration(configFile); - - // Update the new config with missing key values - updateConfigWithMissingKeyValues(config, missingKeyValues); + public void reloadConfigFile() { + // Handle config.yml + handleFile("config.yml", this::loadConfigFile); - postOffice.customBarrelName = getString(config, "custom-barrel-name", "pobox"); - postOffice.language.cantStackItems = getString(config, "cant-stack-items", postOffice.language.cantStackItems); - postOffice.language.removeItemError = getString(config, "remove-item-error", postOffice.language.removeItemError); - postOffice.language.offHandError = getString(config, "offhand-error", postOffice.language.offHandError); - postOffice.language.hotBarError = getString(config, "hotbar-error", postOffice.language.hotBarError); - postOffice.language.sentMessage = getString(config, "sent-message", postOffice.language.sentMessage); - postOffice.language.receivedMessage = getString(config, "received-message", postOffice.language.receivedMessage); - postOffice.language.gotMailMessage = getString(config, "got-mail-message", postOffice.language.gotMailMessage); - postOffice.language.createError = getString(config, "create-error", postOffice.language.createError); - postOffice.language.breakError = getString(config, "break-error", postOffice.language.breakError); - postOffice.language.postboxCreated = getString(config, "postbox-created", postOffice.language.postboxCreated); - postOffice.language.pluginUpToDate = getString(config, "plugin-up-to-date", postOffice.language.pluginUpToDate); + // Handle lang.yml + handleFile("lang.yml", this::loadLangFile); + } - postOffice.postBoxProtection = getBoolean(config, "postbox-protection", true); - postOffice.updateNotificationEnabled = getBoolean(config, "update-notification", true); - postOffice.consoleLogs = getBoolean(config, "console-logs", true); - postOffice.gotMailDelay = getBoolean(config, "got-mail-delay", true); + private void handleFile(String fileName, Runnable loadAction) { + File file = new File(postOffice.getDataFolder(), fileName); + if (!file.exists()) { + postOffice.getLogger().info(fileName + " not found. Creating a new one..."); + saveDefaultConfig(fileName, file); // Create default file if not exists + } else { + postOffice.getLogger().info(fileName + " found. Reloading and checking for missing keys..."); + loadAction.run(); + } + } - } + private void loadConfigFile() { + File configFile = new File(postOffice.getDataFolder(), "config.yml"); + FileConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (checkForMissingConfigKeys(config)) { + postOffice.getLogger().info("Updating config with missing keys..."); + updateConfigWithMissingKeyValues(config, saveMissingConfigKey(config), configFile); + } + postOffice.customBarrelName = getString(config, "custom-barrel-name", "pobox"); + postOffice.postBoxProtection = getBoolean(config, "postbox-protection", true); + postOffice.updateNotificationEnabled = getBoolean(config, "update-notification", true); + postOffice.consoleLogs = getBoolean(config, "console-logs", false); + postOffice.gotMailDelay = getBoolean(config, "got-mail-delay", true); + postOffice.signNotification = getBoolean(config, "sign-notification", true); + } - } catch (Exception e) { - postOffice.getLogger().log(Level.SEVERE, "An error occurred while reloading the config file", e); + private void loadLangFile() { + File langFile = new File(postOffice.getDataFolder(), "lang.yml"); + FileConfiguration langConfig = YamlConfiguration.loadConfiguration(langFile); + if (checkForMissingLangKeys(langConfig)) { + postOffice.getLogger().info("Updating lang with missing keys..."); + updateConfigWithMissingKeyValues(langConfig, saveMissingLangKey(langConfig), langFile); } + postOffice.language.sentMessage = getString(langConfig, "sent-message", postOffice.language.sentMessage); + postOffice.language.receivedMessage = getString(langConfig, "received-message", postOffice.language.receivedMessage); + postOffice.language.gotMailMessage = getString(langConfig, "got-mail-message", postOffice.language.gotMailMessage); + postOffice.language.noPermission = getString(langConfig, "no-permission", postOffice.language.noPermission); + postOffice.language.denyAction = getString(langConfig, "deny-action", postOffice.language.denyAction); + postOffice.language.notRegistered = getString(langConfig, "not-registered", postOffice.language.notRegistered); + postOffice.language.postBoxRemoved = getString(langConfig, "post-box-removed", postOffice.language.postBoxRemoved); + postOffice.language.successfulRegistration = getString(langConfig, "successful-registration", postOffice.language.successfulRegistration); + postOffice.language.alreadyRegistered = getString(langConfig, "already-registered", postOffice.language.alreadyRegistered); + postOffice.language.postboxCreated = getString(langConfig, "postbox-created", postOffice.language.postboxCreated); + postOffice.language.removeFromConfig = getString(langConfig, "remove-from-config", postOffice.language.removeFromConfig); + postOffice.language.lookAtPostBox = getString(langConfig, "look-at-post-box", postOffice.language.lookAtPostBox); + postOffice.language.signOnBarrel = getString(langConfig, "sign-on-barrel", postOffice.language.signOnBarrel); + postOffice.language.alreadyClaimed = getString(langConfig, "already-claimed", postOffice.language.alreadyClaimed); + postOffice.language.invalidPostbox = getString(langConfig, "invalid-postbox", postOffice.language.invalidPostbox); + postOffice.language.successfullyClaimed = getString(langConfig, "successfully-claimed", postOffice.language.successfullyClaimed); + postOffice.language.modifySign = getString(langConfig, "modify-sign", postOffice.language.modifySign); + postOffice.language.unclaimedPostbox = getString(langConfig, "unclaimed-postbox", postOffice.language.unclaimedPostbox); + postOffice.language.userBanned = getString(langConfig, "user-banned", postOffice.language.userBanned); + postOffice.language.postBoxOwner = getString(langConfig, "post-box-owner", postOffice.language.postBoxOwner); + postOffice.language.claimedFor = getString(langConfig, "claimed-for", postOffice.language.claimedFor); + postOffice.language.alreadyHasPostBox = getString(langConfig, "already-has-postbox", postOffice.language.alreadyHasPostBox); + postOffice.language.notPlayedBefore = getString(langConfig, "not-played-before", postOffice.language.notPlayedBefore); + postOffice.language.claimedForOtherPlayer = getString(langConfig, "claimed-for-other-player", postOffice.language.claimedForOtherPlayer); + postOffice.language.pluginUpToDate = getString(langConfig, "plugin-up-to-date", postOffice.language.pluginUpToDate); } - private boolean checkForMissingKeys(FileConfiguration config) { - boolean keysMissing = false; + private boolean checkForMissingConfigKeys(FileConfiguration config) { + return checkForMissingKeys(config, Arrays.asList( + "custom-barrel-name", "sign-notification", "got-mail-delay", "update-notification", "postbox-protection", "console-logs" + )); + } - // List of keys to check - List keysToCheck = Arrays.asList( - "custom-barrel-name", "cant-stack-items", "remove-item-error", "offhand-error", "hotbar-error", "drop-item-error", - "sent-message", "received-message", "got-mail-message", "update-notification", "postbox-protection", - "create-error", "break-error", "console-logs", "postbox-created", "plugin-up-to-date", "got-mail-delay"); + private boolean checkForMissingLangKeys(FileConfiguration config) { + return checkForMissingKeys(config, Arrays.asList( + "sent-message", "received-message", "got-mail-message", "no-permission", "deny-action", + "not-registered", "post-box-removed", "successful-registration", "already-registered", + "postbox-created", "remove-from-config", "look-at-post-box", "sign-on-barrel", + "already-claimed", "invalid-postbox", "successfully-claimed", "modify-sign", + "unclaimed-postbox", "user-banned", "post-box-owner", "claimed-for", + "already-has-postbox", "not-played-before", "claimed-for-other-player", "plugin-up-to-date" + )); + } - // Check for missing keys + private boolean checkForMissingKeys(FileConfiguration config, List keysToCheck) { + boolean keysMissing = false; for (String key : keysToCheck) { if (!config.contains(key)) { - postOffice.getLogger().warning("Key '" + key + "' not found in the configuration file, reverting to the default."); + postOffice.getLogger().warning("Key '" + key + "' not found in the file, reverting to the default."); keysMissing = true; } } return keysMissing; } - private Map saveMissingKeyValues(FileConfiguration config) { - Map missingKeyValues = new HashMap<>(); + private Map saveMissingConfigKey(FileConfiguration config) { + return saveMissingKeys(config, Arrays.asList( + "custom-barrel-name", "sign-notification", "got-mail-delay", "update-notification", "postbox-protection", "console-logs" + )); + } - // List of keys to check - List keysToCheck = Arrays.asList( - "custom-barrel-name", "cant-stack-items", "remove-item-error", "offhand-error", "hotbar-error", "drop-item-error", - "sent-message", "received-message", "got-mail-message", "update-notification", "postbox-protection", - "create-error", "break-error", "console-logs", "postbox-created", "plugin-up-to-date", "got-mail-delay"); + private Map saveMissingLangKey(FileConfiguration config) { + return saveMissingKeys(config, Arrays.asList( + "sent-message", "received-message", "got-mail-message", "no-permission", "deny-action", + "not-registered", "post-box-removed", "successful-registration", "already-registered", + "postbox-created", "remove-from-config", "look-at-post-box", "sign-on-barrel", + "already-claimed", "invalid-postbox", "successfully-claimed", "modify-sign", + "unclaimed-postbox", "user-banned", "post-box-owner", "claimed-for", + "already-has-postbox", "not-played-before", "claimed-for-other-player", "plugin-up-to-date" + )); + } - // Save existing values of missing keys + private Map saveMissingKeys(FileConfiguration config, List keysToCheck) { + Map missingKeyValues = new HashMap<>(); for (String key : keysToCheck) { if (config.contains(key)) { - Object value = config.get(key); - missingKeyValues.put(key, value); + missingKeyValues.put(key, config.get(key)); } } return missingKeyValues; } - private void updateConfigWithMissingKeyValues(FileConfiguration config, Map missingKeyValues) { - // Update the new config with missing key values + private void updateConfigWithMissingKeyValues(FileConfiguration config, Map missingKeyValues, File destination) { for (Map.Entry entry : missingKeyValues.entrySet()) { config.set(entry.getKey(), entry.getValue()); } - - // Save the updated config - saveConfigSilently(config); + saveConfigSilently(config, destination); // Pass the correct file to save to } private void saveDefaultConfig(String resourceName, File destination) { - try (InputStream resourceStream = getClass().getResourceAsStream("/config/" + resourceName)) { + try (InputStream resourceStream = getClass().getResourceAsStream("/" + resourceName)) { if (resourceStream != null) { Files.copy(resourceStream, destination.toPath(), StandardCopyOption.REPLACE_EXISTING); - //getLogger().info("Default config file created successfully."); } else { - postOffice.getLogger().warning("Failed to create default config file. Resource not found."); + postOffice.getLogger().warning("Failed to create default " + resourceName + ". Resource not found."); } } catch (IOException e) { - postOffice.getLogger().log(Level.SEVERE, "Error creating default config file", e); + postOffice.getLogger().log(Level.SEVERE, "Error creating default file: " + resourceName, e); } } private String getString(FileConfiguration config, String key, String defaultValue) { - if (config.contains(key) && config.isString(key)) { - String originalValue = config.getString(key); - assert originalValue != null; - String updatedValue = originalValue.replaceAll("(?m)^\\s+|\\s+$", "") // Remove leading/trailing spaces, tabs, and indentation - .replaceAll("\\s+", " "); // Collapse multiple spaces into a single space - - // Log removal to the console if changes were made - if (!originalValue.equals(updatedValue)) { - if (postOffice.consoleLogs) { - postOffice.getLogger().info("Extra spaces removed from key '" + key + "'"); - } - } - - - // Check for a string split across two lines - if (!originalValue.equals(updatedValue) && originalValue.contains("\n")) { - updatedValue = originalValue.replace("\n", ""); // Remove newline characters - if (postOffice.consoleLogs) { - postOffice.getLogger().info("Indentation removed from key '" + key + "'"); - } - } - - - - // Save the updated value back to the config - config.set(key, updatedValue); - saveConfigSilently(config); // Custom method to save the configuration without logging exceptions - - return updatedValue; - } else { - // Log a warning if the key is not found or is of unexpected type - postOffice.getLogger().warning("Key '" + key + "' not found in the configuration file, reverting to the default."); - - // Set the default value in the configuration - config.set(key, defaultValue); - - // Save the configuration with the default value - saveConfigSilently(config); // Custom method to save the configuration without logging exceptions - - return defaultValue; - } + return config.getString(key, defaultValue); } private boolean getBoolean(FileConfiguration config, String key, boolean defaultValue) { - if (config.contains(key) && config.isBoolean(key)) { - boolean originalValue = config.getBoolean(key); - - // Save the updated value back to the config - config.set(key, originalValue); - saveConfigSilently(config); // Custom method to save the configuration without logging exceptions - - return originalValue; - } else { - // Log a warning if the key is not found or is of unexpected type - postOffice.getLogger().warning("Key '" + key + "' not found in the configuration file, reverting to the default."); - - // Set the default value in the configuration - config.set(key, defaultValue); - - // Save the configuration with the default value - saveConfigSilently(config); // Custom method to save the configuration without logging exceptions - - return defaultValue; - } + return config.getBoolean(key, defaultValue); } - private void saveConfigSilently(FileConfiguration config) { + private void saveConfigSilently(FileConfiguration config, File destination) { try { - config.save(new File(postOffice.getDataFolder(), "config.yml")); + config.save(destination); } catch (IOException e) { - // Log the exception or handle it as needed (e.g., printStackTrace()) - } - } - - public void setCustomBarrelName(String newCustomBarrelName) { - try { - File configFile = new File(postOffice.getDataFolder(), "config.yml"); - FileConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Update the custom barrel name in the configuration - config.set("custom-barrel-name", newCustomBarrelName); - - // Save the updated configuration - saveConfigSilently(config); - - // Update the custom barrel name in the PostOffice instance - postOffice.customBarrelName = newCustomBarrelName; - - } catch (Exception e) { - postOffice.getLogger().log(Level.SEVERE, "An error occurred while updating the custom barrel name", e); + postOffice.getLogger().log(Level.SEVERE, "An error occurred while saving the file " + destination.getName(), e); } } diff --git a/src/main/java/io/shantek/functions/TabCompleter.java b/src/main/java/io/shantek/functions/TabCompleter.java index d81b585..bec2fa7 100644 --- a/src/main/java/io/shantek/functions/TabCompleter.java +++ b/src/main/java/io/shantek/functions/TabCompleter.java @@ -1,15 +1,20 @@ package io.shantek.functions; import io.shantek.PostOffice; +import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class TabCompleter implements org.bukkit.command.TabCompleter { public PostOffice postOffice; + public TabCompleter(PostOffice postOffice) { this.postOffice = postOffice; } @@ -20,13 +25,40 @@ public List onTabComplete(CommandSender sender, Command cmd, String labe if (cmd.getName().equalsIgnoreCase("postoffice")) { if (args.length == 1) { - // Check the first argument - if ("reload".startsWith(args[0].toLowerCase()) && sender.hasPermission("shantek.postoffice.reload")) { + // Add commands based on permissions + if (sender.hasPermission("shantek.postoffice.reload")) { completions.add("reload"); } + if (sender.hasPermission("shantek.postoffice.claim") || sender.hasPermission("shantek.postoffice.claim.others")) { + completions.add("claim"); + } + if (sender.hasPermission("shantek.postoffice.register")) { + completions.add("register"); + completions.add("remove"); + } + + // Commands all players have access to + completions.add("info"); + + // Filter completions based on partial input + return completions.stream() + .filter(c -> c.startsWith(args[0].toLowerCase())) + .sorted() + .collect(Collectors.toList()); + } else if (args.length == 2 && args[0].equalsIgnoreCase("claim") && sender.hasPermission("shantek.postoffice.claim.others")) { + // Populate online players' names for claim.others permission + for (Player player : Bukkit.getOnlinePlayers()) { + completions.add(player.getName()); + } + + // Filter players based on partial input + return completions.stream() + .filter(c -> c.toLowerCase().startsWith(args[1].toLowerCase())) + .sorted() + .collect(Collectors.toList()); } } - return completions; - } + return Collections.emptyList(); + } } diff --git a/src/main/java/io/shantek/functions/UpdateChecker.java b/src/main/java/io/shantek/functions/UpdateChecker.java index 788b4ad..012128d 100644 --- a/src/main/java/io/shantek/functions/UpdateChecker.java +++ b/src/main/java/io/shantek/functions/UpdateChecker.java @@ -11,7 +11,7 @@ import java.util.logging.Level; public class UpdateChecker { - public String currentVersion = null; + public static String remoteVersion = null; public static void checkForUpdatesAsync(String currentVersion, Plugin plugin) { CompletableFuture.runAsync(() -> checkForUpdates(currentVersion, plugin)); @@ -54,7 +54,44 @@ private static void checkForUpdates(String currentVersion, Plugin plugin) { } public static boolean isNewVersionAvailable(String currentVersion, String remoteVersion) { - // Directly compare the version strings - return !currentVersion.equals(remoteVersion); + // Check if the remote version is null, return false in that case + if (remoteVersion == null || remoteVersion.isEmpty()) { + return false; + } + + // Split the version strings by '.' to compare major, minor, and patch + String[] currentVersionParts = currentVersion.split("\\."); + String[] remoteVersionParts = remoteVersion.split("\\."); + + // Check if current version is a dev build (contains "-SNAPSHOT" or other pre-release indicators) + boolean isDevBuild = currentVersion.toLowerCase().contains("snapshot") || currentVersion.toLowerCase().contains("dev"); + + // Compare major, minor, and patch versions + for (int i = 0; i < Math.min(currentVersionParts.length, remoteVersionParts.length); i++) { + try { + int currentPart = Integer.parseInt(currentVersionParts[i]); + int remotePart = Integer.parseInt(remoteVersionParts[i]); + + // If the current version part is less than the remote, a new version is available + if (currentPart < remotePart) { + return true; + } + // If the current version part is greater, the current version is newer (possibly a dev build) + if (currentPart > remotePart) { + return false; + } + } catch (NumberFormatException e) { + // In case of non-numeric version parts (like "1.6.2-SNAPSHOT"), treat dev build as newer + if (isDevBuild) { + return false; // Consider dev builds to be newer than any stable release + } + } + } + + // If all version parts are equal, but one version has more parts (e.g., "1.6" vs "1.6.1"), compare lengths + return currentVersionParts.length < remoteVersionParts.length; + // Remote version has more parts (e.g., 1.6 -> 1.6.1), so a new version is available + // No new version available } + } \ No newline at end of file diff --git a/src/main/java/io/shantek/listeners/BarrelProtection.java b/src/main/java/io/shantek/listeners/BarrelProtection.java index d88df86..4de2800 100644 --- a/src/main/java/io/shantek/listeners/BarrelProtection.java +++ b/src/main/java/io/shantek/listeners/BarrelProtection.java @@ -3,6 +3,7 @@ import io.shantek.PostOffice; import org.bukkit.ChatColor; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.block.*; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -37,17 +38,15 @@ public void onSignChange(SignChangeEvent event) { Player player = event.getPlayer(); Block signBlock = event.getBlock(); - if (hasBarrelNearby(signBlock)) { - if (!player.isOp() && !player.hasPermission("shantek.postoffice.create")) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.createError)); + // Get the attached barrel from the sign + Block attachedBarrel = postOffice.helpers.getAttachedBarrel(signBlock); + + // Ensure the attached block is a barrel and that it is in the config + if (attachedBarrel != null && attachedBarrel.getType() == Material.BARREL) { + if (postOffice.helpers.isBarrelInConfig(attachedBarrel)) { + // Cancel the sign change event if the barrel is in the config (protected post box) + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.modifySign)); event.setCancelled(true); - } else { - String line2 = event.getLine(1); - if (line2 != null && !line2.isEmpty()) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', - postOffice.language.postboxCreated - .replace("%username%", line2))); - } } } } @@ -61,26 +60,37 @@ public void onBlockBreak(BlockBreakEvent event) { Player player = event.getPlayer(); Block brokenBlock = event.getBlock(); - // Check if the broken block is a barrel - if (brokenBlock.getState() instanceof Barrel) { - Barrel barrel = (Barrel) brokenBlock.getState(); - String barrelCustomName = barrel.getCustomName(); + // Check if the broken block is a protected post box (either a barrel or a sign) + if (postOffice.helpers.isProtectedPostBox(brokenBlock)) { + if (player.isOp() || player.hasPermission("shantek.postoffice.break")) { + Block barrelBlock = null; + + // Check if the player is breaking a sign + if (Tag.SIGNS.isTagged(brokenBlock.getType())) { + // Retrieve the attached barrel if the broken block is a sign + barrelBlock = postOffice.helpers.getAttachedBarrel(brokenBlock); + } else if (brokenBlock.getType() == Material.BARREL) { + // If breaking a barrel directly, set it as the barrelBlock + barrelBlock = brokenBlock; + } - if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { - if (!player.isOp() && !player.hasPermission("shantek.postoffice.break")) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.breakError)); - event.setCancelled(true); + // Ensure barrelBlock is valid before proceeding + if (barrelBlock == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.notRegistered)); + return; } - } - } - // Check if the broken block is a sign - if (brokenBlock.getState() instanceof Sign) { - if (hasBarrelNearby(brokenBlock)) { - if (!player.isOp() && !player.hasPermission("shantek.postoffice.break")) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.breakError)); - event.setCancelled(true); + // Check if the barrel exists in the config (registered post box) + if (postOffice.helpers.isBarrelInConfig(barrelBlock)) { + // Call the helper to remove the barrel from the cache and config + postOffice.helpers.removeBarrelFromCache(barrelBlock); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.removeFromConfig)); } + + } else { + // Prevent the player from breaking the post box if they don't have permission + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.noPermission)); + event.setCancelled(true); } } } @@ -94,42 +104,14 @@ public void onBlockPlace(BlockPlaceEvent event) { Player player = event.getPlayer(); Block placedBlock = event.getBlockPlaced(); - if (placedBlock.getState() instanceof Sign && hasBarrelNearby(placedBlock)) { + if (placedBlock.getState() instanceof Sign && postOffice.helpers.hasBarrelNearby(placedBlock)) { if (!player.isOp() && !player.hasPermission("shantek.postoffice.create")) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.createError)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.noPermission)); placedBlock.breakNaturally(); } } } - private boolean hasBarrelNearby(Block block) { - // Check if the given block is a barrel with the custom name - if (block.getType() == Material.BARREL) { - Barrel barrel = (Barrel) block.getState(); - String barrelCustomName = barrel.getCustomName(); - - if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { - return true; - } - } - - // Check if any nearby block is a barrel with the custom name - for (BlockFace blockFace : BlockFace.values()) { - Block relativeBlock = block.getRelative(blockFace); - - if (relativeBlock.getType() == Material.BARREL) { - Barrel barrel = (Barrel) relativeBlock.getState(); - String barrelCustomName = barrel.getCustomName(); - - if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { - return true; - } - } - } - - return false; - } - @EventHandler public void onInventoryMoveItem(InventoryMoveItemEvent event) { if (!postOffice.postBoxProtection) { @@ -142,8 +124,7 @@ public void onInventoryMoveItem(InventoryMoveItemEvent event) { // Check if the source is a barrel and the destination is a hopper or hopper minecart if (sourceHolder instanceof Barrel && (destinationHolder instanceof Hopper || destinationHolder instanceof HopperMinecart)) { Barrel barrel = (Barrel) sourceHolder; - String barrelCustomName = barrel.getCustomName(); - if (barrelCustomName != null && barrelCustomName.equalsIgnoreCase(postOffice.customBarrelName)) { + if (postOffice.helpers.isProtectedPostBox(barrel.getBlock())) { event.setCancelled(true); } } diff --git a/src/main/java/io/shantek/listeners/InventoryClick.java b/src/main/java/io/shantek/listeners/InventoryClick.java index e6ccedf..a25434e 100644 --- a/src/main/java/io/shantek/listeners/InventoryClick.java +++ b/src/main/java/io/shantek/listeners/InventoryClick.java @@ -1,5 +1,6 @@ package io.shantek.listeners; +import io.shantek.PostOffice; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; @@ -14,13 +15,13 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import io.shantek.PostOffice; import java.util.Objects; public class InventoryClick implements Listener { public PostOffice postOffice; + public InventoryClick(PostOffice postOffice) { this.postOffice = postOffice; } @@ -32,7 +33,7 @@ public void onInventoryClick(InventoryClickEvent event) { Inventory clickedInventory = event.getClickedInventory(); // If they aren't opening a barrel, ignore it - if (event.getClickedInventory() == null || inventory.getType() != InventoryType.BARREL) { + if (clickedInventory == null || inventory.getType() != InventoryType.BARREL) { return; } @@ -49,7 +50,6 @@ public void onInventoryClick(InventoryClickEvent event) { Barrel barrel = (Barrel) blockState; if (barrel.getCustomName() != null && barrel.getCustomName().equalsIgnoreCase(postOffice.customBarrelName)) { event.setCancelled(true); - //player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.removeItemError)); return; } } @@ -58,7 +58,7 @@ public void onInventoryClick(InventoryClickEvent event) { } } - Block clickedBlock = Objects.requireNonNull(event.getClickedInventory().getLocation()).getBlock(); + Block clickedBlock = Objects.requireNonNull(clickedInventory.getLocation()).getBlock(); if (clickedBlock.getType() == Material.BARREL) { BlockState blockState = clickedBlock.getState(); @@ -85,28 +85,28 @@ public void onInventoryClick(InventoryClickEvent event) { boolean isNotOwner = !ownerName.equalsIgnoreCase(player.getName()); - // Prevent non-owners who do not have OP from taking items or shift-clicking - if (!player.isOp() && isNotOwner && !player.hasPermission("shantek.postoffice.removeitems") && (event.getAction().name().contains("PICKUP") || event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY)) { - event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.removeItemError)); - return; + // Prevent non-owners from taking items out of the postbox + if (!player.isOp() && isNotOwner && !player.hasPermission("shantek.postoffice.removeitems")) { + if (event.getAction().name().contains("PICKUP") || event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) { + event.setCancelled(true); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); + return; + } } - // If player is not the owner and trying to take an item, cancel the event - if (isNotOwner && !player.hasPermission("shantek.postoffice.removeitems") && event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) { - event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.removeItemError)); + // Allow placing items into the postbox + if (event.getAction() == InventoryAction.PLACE_ALL || event.getAction() == InventoryAction.PLACE_ONE || + event.getAction() == InventoryAction.PLACE_SOME || event.getAction() == InventoryAction.SWAP_WITH_CURSOR) { return; } - if (isNotOwner && !player.hasPermission("shantek.postoffice.removeitems") && !player.isOp()) { - // CHECK IF THE PLAYER DOESN'T HAVE PERMISSION TO USE THIS BARREL, RESTRICT NUMBER KEY CLICKING TO MOVE TO HOTBAR - if (event.getWhoClicked() instanceof Player && event.getClickedInventory() != null) { - ItemStack hotbarItem = event.getClick() == ClickType.NUMBER_KEY ? event.getWhoClicked().getInventory().getItem(event.getHotbarButton()) : null; - + // Prevent item swapping using hotbar for unauthorized players + if (isNotOwner && !player.hasPermission("shantek.postoffice.removeitems")) { + if (event.getClick() == ClickType.NUMBER_KEY) { + ItemStack hotbarItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton()); if (hotbarItem != null) { event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.hotBarError)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); return; } } @@ -114,20 +114,20 @@ public void onInventoryClick(InventoryClickEvent event) { // Prevent players from swapping items from a barrel while having another item in hand if (event.getAction() == InventoryAction.SWAP_WITH_CURSOR) { event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.removeItemError)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); return; } if (event.getAction() == InventoryAction.HOTBAR_SWAP) { event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.hotBarError)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); return; } // Prevent the player from dropping items out of the postbox if (event.getClick() == ClickType.DROP || event.getClick() == ClickType.CONTROL_DROP) { event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.dropItemError)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); return; } } @@ -139,7 +139,7 @@ public void onInventoryClick(InventoryClickEvent event) { // If the player is not the owner and trying to add to an existing stack, cancel the event if (isNotOwner && !player.hasPermission("shantek.postoffice.removeitems") && event.getAction() == InventoryAction.PLACE_ALL && item.getAmount() < item.getMaxStackSize()) { event.setCancelled(true); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.cantStackItems)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.denyAction)); return; } break; @@ -149,4 +149,4 @@ public void onInventoryClick(InventoryClickEvent event) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/io/shantek/listeners/InventoryClose.java b/src/main/java/io/shantek/listeners/InventoryClose.java index 6983d52..f8ea36e 100644 --- a/src/main/java/io/shantek/listeners/InventoryClose.java +++ b/src/main/java/io/shantek/listeners/InventoryClose.java @@ -1,10 +1,7 @@ package io.shantek.listeners; import io.shantek.PostOffice; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; +import org.bukkit.*; import org.bukkit.block.*; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -26,71 +23,77 @@ public InventoryClose(PostOffice postOffice) { @EventHandler public void onInventoryClose(InventoryCloseEvent event) { - PostOffice plugin = PostOffice.getInstance(); Inventory inventory = event.getInventory(); - Player player = (Player) event.getPlayer(); + + // Check if the inventory is a barrel if (inventory.getType() == InventoryType.BARREL) { Block clickedBlock = Objects.requireNonNull(event.getInventory().getLocation()).getBlock(); if (clickedBlock.getType() == Material.BARREL) { - BlockState blockState = clickedBlock.getState(); + if (postOffice.helpers.isBarrelInConfig(clickedBlock)) { - if (blockState instanceof Barrel) { - Barrel barrel = (Barrel) blockState; + // This barrel is in the config, treat it as a valid post box + UUID boxOwnerUUID = postOffice.helpers.getOwnerUUID(clickedBlock); - if (barrel.getCustomName() != null && barrel.getCustomName().equalsIgnoreCase(postOffice.customBarrelName)) { - boolean isOwner = false; - String ownerName = ""; + if (boxOwnerUUID == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.unclaimedPostbox)); + } else { - for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}) { - Block relativeBlock = clickedBlock.getRelative(face); + if (postOffice.consoleLogs) { + OfflinePlayer boxOwner = postOffice.helpers.getPlayer(boxOwnerUUID); + plugin.getLogger().info("Closing valid post box. Owner: " + boxOwner.getName()); + } - if (relativeBlock.getType().name().toUpperCase().contains("SIGN")) { - Sign sign = (Sign) relativeBlock.getState(); - ownerName = sign.getLine(1); + } - if (sign.getLine(1).equalsIgnoreCase(event.getPlayer().getName())) { - isOwner = true; - break; - } - } + // Check if the player owns the post box + if (boxOwnerUUID != null && postOffice.helpers.isPostBoxOwner(clickedBlock, player)) { + // Player owns the post box - clear the "You have mail" message from the sign + Block signBlock = postOffice.helpers.getSignFromConfig(clickedBlock); + if (signBlock != null && signBlock.getState() instanceof Sign) { + Sign sign = (Sign) signBlock.getState(); + sign.setLine(2, ""); // Clear the 3rd line + sign.update(); } - if (!ownerName.isEmpty()) { - if (!isOwner) { - postOffice.newItemCount = postOffice.helpers.countNonNullItems(inventory.getContents()); - if (postOffice.newItemCount > postOffice.previousItemCount) { - - player.sendMessage(ChatColor.translateAlternateColorCodes('&', - postOffice.language.sentMessage - .replace("%sender%", player.getName()) - .replace("%receiver%", ownerName))); - - // Get owner UUID - OfflinePlayer postBoxOwner = Bukkit.getOfflinePlayer(ownerName); - UUID ownerUUID = postBoxOwner.getUniqueId(); - - // Add owners to mail list if someone else is adding items - if (postOffice.consoleLogs) { - plugin.getLogger().info(player.getName() + " added mail for " + ownerName); - } - postOffice.playersWithMail.add(ownerName); - postOffice.helpers.saveMailFile(); - - Player owner = Bukkit.getPlayer(ownerName); - if (owner != null) { - owner.sendMessage(ChatColor.translateAlternateColorCodes('&', - postOffice.language.receivedMessage - .replace("%sender%", player.getName()) - .replace("%receiver%", owner.getName()))); - } - } - } else { - // If they were the owner, and it was their barrel, remove them from the mail list - postOffice.playersWithMail.remove(event.getPlayer().getName()); - postOffice.helpers.saveMailFile(); + // Remove any mail notifications (from the internal mail list) + postOffice.playersWithMail.remove(player.getUniqueId().toString()); + postOffice.helpers.saveMailFile(); + + } else if (boxOwnerUUID != null) { + // Player does not own the post box - check for item changes + postOffice.newItemCount = postOffice.helpers.countNonNullItems(inventory.getContents()); + if (postOffice.newItemCount > postOffice.previousItemCount) { + // Update the sign to notify the owner of mail + Block signBlock = postOffice.helpers.getSignFromConfig(clickedBlock); + if (postOffice.signNotification && signBlock != null && signBlock.getState() instanceof Sign) { + Sign sign = (Sign) signBlock.getState(); + sign.setLine(2, ChatColor.GREEN + "You have mail"); // Set "You have mail" on the 3rd line + sign.update(); + } + + // Send message to the player + player.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.sentMessage + .replace("%sender%", player.getName()) + .replace("%receiver%", Bukkit.getOfflinePlayer(boxOwnerUUID).getName()))); + + // Add the owner to the mail list + if (postOffice.consoleLogs) { + plugin.getLogger().info(player.getName() + " added mail for " + Bukkit.getOfflinePlayer(boxOwnerUUID).getName()); + } + postOffice.playersWithMail.add(boxOwnerUUID.toString()); + postOffice.helpers.saveMailFile(); + + // Notify the owner if they are online + Player owner = Bukkit.getPlayer(boxOwnerUUID); + if (owner != null) { + owner.sendMessage(ChatColor.translateAlternateColorCodes('&', + postOffice.language.receivedMessage + .replace("%sender%", player.getName()) + .replace("%receiver%", owner.getName()))); } } } @@ -98,4 +101,8 @@ public void onInventoryClose(InventoryCloseEvent event) { } } } -} \ No newline at end of file + +} + + + diff --git a/src/main/java/io/shantek/listeners/InventoryOpen.java b/src/main/java/io/shantek/listeners/InventoryOpen.java index 294d377..4966f55 100644 --- a/src/main/java/io/shantek/listeners/InventoryOpen.java +++ b/src/main/java/io/shantek/listeners/InventoryOpen.java @@ -1,10 +1,11 @@ package io.shantek.listeners; import io.shantek.PostOffice; -import org.bukkit.Material; +import org.bukkit.ChatColor; import org.bukkit.block.Barrel; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryOpenEvent; @@ -15,6 +16,7 @@ public class InventoryOpen implements Listener { public PostOffice postOffice; + public InventoryOpen(PostOffice postOffice) { this.postOffice = postOffice; } @@ -23,22 +25,31 @@ public InventoryOpen(PostOffice postOffice) { public void onInventoryOpen(InventoryOpenEvent event) { Inventory inventory = event.getInventory(); + Player player = (Player) event.getPlayer(); if (inventory.getType() == InventoryType.BARREL) { Block clickedBlock = Objects.requireNonNull(inventory.getLocation()).getBlock(); - if (clickedBlock.getType() == Material.BARREL) { - BlockState blockState = clickedBlock.getState(); - if (blockState instanceof Barrel) { - Barrel barrel = (Barrel) blockState; + BlockState blockState = clickedBlock.getState(); + + if (blockState instanceof Barrel) { + Barrel barrel = (Barrel) blockState; + + if (barrel.getCustomName() != null && barrel.getCustomName().equalsIgnoreCase(postOffice.customBarrelName)) { - if (barrel.getCustomName() != null && barrel.getCustomName().equalsIgnoreCase(postOffice.customBarrelName)) { + if (player.hasPermission("shantek.postoffice.use")) { + // They have permission to use the post office system. Let them open the post box postOffice.previousItemCount = postOffice.helpers.countNonNullItems(inventory.getContents()); + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', postOffice.language.userBanned)); + event.setCancelled(true); } + } } + } } diff --git a/src/main/java/io/shantek/listeners/PlayerJoin.java b/src/main/java/io/shantek/listeners/PlayerJoin.java index 8cd7028..6be27e7 100644 --- a/src/main/java/io/shantek/listeners/PlayerJoin.java +++ b/src/main/java/io/shantek/listeners/PlayerJoin.java @@ -22,7 +22,7 @@ public void onPlayerLogin(PlayerJoinEvent event) { Player player = event.getPlayer(); - if (postOffice.playersWithMail.contains(player.getName())) { + if (postOffice.playersWithMail.contains(player.getUniqueId().toString())) { // Set initial delay of 1 second long messageDelay = 20L; @@ -38,8 +38,12 @@ public void onPlayerLogin(PlayerJoinEvent event) { } if (postOffice.updateNotificationEnabled && (player.isOp() || player.hasPermission("shantek.postoffice.updatenotification"))) { - if (UpdateChecker.isNewVersionAvailable(postOffice.getDescription().getVersion(), UpdateChecker.remoteVersion)) { - player.sendMessage("[Post Office] An update is available! New version: " + UpdateChecker.remoteVersion); + String currentVersion = postOffice.getDescription().getVersion(); + String remoteVersion = UpdateChecker.remoteVersion; // Assuming UpdateChecker retrieves the remote version + + // Only notify if a newer version is available + if (UpdateChecker.isNewVersionAvailable(currentVersion, remoteVersion)) { + player.sendMessage("[Post Office] An update is available! New version: " + remoteVersion); } } diff --git a/src/main/resources/barrels.yml b/src/main/resources/barrels.yml new file mode 100644 index 0000000..209d1c6 --- /dev/null +++ b/src/main/resources/barrels.yml @@ -0,0 +1,8 @@ +# +# This config stores the location and owner of all post box barrels on your server. +# +# Never manually edit this file as it's managed by the plugin when creating +# or destroying post boxes. +# + +barrels: \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 62edf35..49c16cb 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -2,47 +2,14 @@ # will be treated as a Post Box custom-barrel-name: "pobox" -# Message sent to the sender when closing the Post Box -sent-message: "&a[Post Office] &aMail sent to %receiver%." - -# Message sent to the receiver if they are online -received-message: "&a[Post Office] &eYou received mail from %sender%!" - -# Message sent to the receiver next time they log on to the server -got-mail-message: "&a[Post Office] &fYou got mail!" +# Display mail state on post box signs. When enabled, a notification will be +# added to the third line of the post box sign +sign-notification: true # Do we want to delay the got-mail-message by 10 seconds? This ensures the message # isn't lost in any other messages they receive during connection got-mail-delay: true -# Error shown when the player attempts to stack items in a post box -cant-stack-items: "&a[Post Office] &4You don't have permission to do that." - -# Error shown when a player attempts to remove items without permission -remove-item-error: "&a[Post Office] &4You don't have permission to remove items." - -# Error shown when trying to use their offhand while in a Post Box -offhand-error: "&a[Post Office] &4No offhand usage while in a Post Box!" - -# Error shown when trying to swap items to their hotbar -hotbar-error: "&a[Post Office] &4No hot bar usage while in a Post Box!" - -# Error shown when the player tries to create a Post Box -create-error: "&a[Post Office] &4You can't create a Post Box." - -# Error shown when a player tries to break a Post Box -break-error: "&a[Post Office] &4You can't break this Post Box." - -# Confirmation message shown when a post box has been created -# You can use %username% to show the players name -postbox-created: "&a[Post Office] &2 Box successfully created for %username%" - -# Error shown to the player if they try dropping an item while in a post box -drop-item-error: "&a[Post Office] &4 You can't drop items while in a postbox." - -# Message shown in the console to inform the server owner the plugin is up to date -plugin-up-to-date: "Your plugin is up to date." - # Do you want to use the built-in protection to prevent players breaking # Post Boxes? Disable if you prefer to use something like WorldGuard postbox-protection: true @@ -54,4 +21,4 @@ update-notification: true # Do you want to print messages to the server console? You'll always receive # start up/shut down messages as well as warnings/errors. Disabling this will # remove the less important console logs like the mail file being updated etc. -console-logs: true \ No newline at end of file +console-logs: false \ No newline at end of file diff --git a/src/main/resources/config/config.yml b/src/main/resources/config/config.yml deleted file mode 100644 index 62edf35..0000000 --- a/src/main/resources/config/config.yml +++ /dev/null @@ -1,57 +0,0 @@ -# The name of your barrel. Any barrel renamed to this in an anvil -# will be treated as a Post Box -custom-barrel-name: "pobox" - -# Message sent to the sender when closing the Post Box -sent-message: "&a[Post Office] &aMail sent to %receiver%." - -# Message sent to the receiver if they are online -received-message: "&a[Post Office] &eYou received mail from %sender%!" - -# Message sent to the receiver next time they log on to the server -got-mail-message: "&a[Post Office] &fYou got mail!" - -# Do we want to delay the got-mail-message by 10 seconds? This ensures the message -# isn't lost in any other messages they receive during connection -got-mail-delay: true - -# Error shown when the player attempts to stack items in a post box -cant-stack-items: "&a[Post Office] &4You don't have permission to do that." - -# Error shown when a player attempts to remove items without permission -remove-item-error: "&a[Post Office] &4You don't have permission to remove items." - -# Error shown when trying to use their offhand while in a Post Box -offhand-error: "&a[Post Office] &4No offhand usage while in a Post Box!" - -# Error shown when trying to swap items to their hotbar -hotbar-error: "&a[Post Office] &4No hot bar usage while in a Post Box!" - -# Error shown when the player tries to create a Post Box -create-error: "&a[Post Office] &4You can't create a Post Box." - -# Error shown when a player tries to break a Post Box -break-error: "&a[Post Office] &4You can't break this Post Box." - -# Confirmation message shown when a post box has been created -# You can use %username% to show the players name -postbox-created: "&a[Post Office] &2 Box successfully created for %username%" - -# Error shown to the player if they try dropping an item while in a post box -drop-item-error: "&a[Post Office] &4 You can't drop items while in a postbox." - -# Message shown in the console to inform the server owner the plugin is up to date -plugin-up-to-date: "Your plugin is up to date." - -# Do you want to use the built-in protection to prevent players breaking -# Post Boxes? Disable if you prefer to use something like WorldGuard -postbox-protection: true - -# Do you want to check for updates when restarting the server? -# This will also message any server ops to notify them each time they log on. -update-notification: true - -# Do you want to print messages to the server console? You'll always receive -# start up/shut down messages as well as warnings/errors. Disabling this will -# remove the less important console logs like the mail file being updated etc. -console-logs: true \ No newline at end of file diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml new file mode 100644 index 0000000..0f5fb6f --- /dev/null +++ b/src/main/resources/lang.yml @@ -0,0 +1,91 @@ + +# Confirmation message shown to the sender when leaving mail in another post box. +# %receiver% will be replaced with the person sending the mail +sent-message: "&a[Post Office] &aMail sent to %receiver%." + +# Message sent to the receiver if they are online +# %receiver% will be replaced with the name of the person receiving the mail +# %sender% will be replaced with the name of the person sending the mail +received-message: "&a[Post Office] &eYou received mail from %sender%!" + +# Message sent to the player next time they log on to the server, if they have mail +got-mail-message: "&a[Post Office] &fYou got mail!" + +# Generic no permission error message when a player is attempting to do an action +# that they shouldn't have permission to do +no-permission: "&a[Post Office] &4You don't have permission to do that." + +# Error shown when trying to use their offhand while in a Post Box +deny-action: "&a[Post Office] &4You can't do that here!" + +# Error when the post box isn't registered in the config. Used in instanced like when trying to remove +# a post box, looking up postbox info etc. +not-registered: "&a[Post Office] &4This isn't a registered post office box." + +# Message shown when a post box is successfully removed with the remove command +post-box-removed: "&a[Post Office] &aPost box removed successfully." + +# Message when a post box has been successfully registered in the config +successful-registration: "&a[Post Office] &aPost box registered successfully." + +# Warning shown to the admin/mod when trying to register a new post box if it's already registered +already-registered: "&a[Post Office] &4This post box is already registered." + +# Confirmation message shown when a post box has been created +# You can use %username% to show the players name +postbox-created: "&a[Post Office] &2 Box successfully created for %username%" + +# Message when a post box has been broken but was still in the config. The plugin will +# automatically remove it from the config. +remove-from-config: "&a[Post Office] &aPost box successfully removed from the config." + +# Error when running a command and not looking at a configured post box. +# This is a generic error, shown in a lot of different scenarios. +look-at-post-box: "&a[Post Office] &4You must be looking at a barrel or a sign attached to a barrel." + +# Warning shown in cases where we haven't found a barrel/sign combo +# resulting in an invalid setup +sign-on-barrel: "&a[Post Office] &4The sign must be attached to a barrel." + +# Message shown when a post box has already been claimed to another player +already-claimed: "&a[Post Office] &4This post box has already been claimed." + +# Error shown when running the info command and not looking at a valid postbox +invalid-postbox: "&a[Post Office] &4This isn't a valid post box." + +# Message shown when a post box is successfully claimed with the claim command +successfully-claimed: "&a[Post Office] &aYou have successfully claimed this post box." + +# Error when a player is trying to modify a post box sign without the correct permission +modify-sign: "&a[Post Office] &4You cannot modify a post box sign." + +# Warning shown when closing an unclaimed post box. Good as a reminder for an admin/mod +# that they may be leaving items in the wrong post box +unclaimed-postbox: "&a[Post Office] &4This post box is unclaimed." + +# Error shown to a user when they have been banned from using the post office plugin. +# They aren't able to open a post box and will see this error. +user-banned: "&a[Post Office] &4You aren't able to interact with this post box." + +# Message shown when looking running the info command and looking at a post box +# %owner% is replaced with the name of the player who owns the post box +post-box-owner: "&a[Post Office] &aThis post box is owned by %owner%" + +# %owner% is replaced with the name of the owner of the post box +claimed-for: "&a[Post Office] &aThis post box has been claimed for %owner%" + +# Warning shown when the player tries to register a box and already has one registered +# %player% is replaced with the name of the player who is trying to claim a box +# %location% is replaced with the location of the box they already own +already-has-postbox: "&a[Post Office] &4%player% already has a post box at: %location%" + +# A warning shown when the name being registered doesn't exist on the server +# %player% is replaced with the name that is being used to register a box +not-played-before: "&a[Post Office] &4The player %player% has not played on this server." + +# Message shown to a player when a post box has been set up for them. Displayed when using the +# claim of other players command. Sent to the player if they are online at the time. +claimed-for-other-player: "&a[Post Office] &aA post box has been created for you." + +# Message shown in the console to inform the server owner the plugin is up to date +plugin-up-to-date: "Your plugin is up to date." \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 91d49eb..14917fe 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,12 +1,44 @@ name: PostOffice api-version: 1.15 -version: 1.6.3 +version: '${project.version}' author: Shantek.io main: io.shantek.PostOffice commands: postoffice: - description: Reload the PostOffice plugin - usage: /postoffice reload - permission: shantek.postoffice.reload - default: op \ No newline at end of file + description: Post office plugin commands + aliases: [po] + permission: shantek.postoffice.use + +permissions: + shantek.postoffice.use: + description: Allows usage of the Post Office commands. + default: true + + shantek.postoffice.register: + description: Allows the player to register a post box in the config. + default: op + + shantek.postoffice.remove: + description: Allows the player to remove a post box from the config file. + default: op + + shantek.postoffice.reload: + description: Allows the player to reload the post office plugin config. + default: op + + shantek.postoffice.claim: + description: Allows players to claim their own post box. + default: op + + shantek.postoffice.claim.others: + description: Allows admin/mods to claim a post box for others. + default: op + + shantek.postoffice.removeitems: + description: Allows admin/mods to remove items from other post boxes. + default: op + + shantek.postoffice.updatenotification: + description: Notifies OPs/admins/mods of a plugin update. + default: op