From 3e247e6cb19e970a4679a5c6df5cb34916d1bb8f Mon Sep 17 00:00:00 2001 From: Thijs Wiefferink Date: Fri, 26 Jan 2018 12:08:22 +0100 Subject: [PATCH] Move sign listeners into SignsFeature - Fix removing signs not removing it from the signsByChunk map --- .../java/me/wiefferink/areashop/AreaShop.java | 6 - .../areashop/commands/AddsignCommand.java | 5 +- .../areashop/commands/DelsignCommand.java | 5 +- .../areashop/commands/ImportJob.java | 6 +- .../areashop/features/FriendsFeature.java | 20 +- .../areashop/features/RegionFeature.java | 20 +- .../areashop/features/SignsFeature.java | 449 ------------------ .../areashop/features/TeleportFeature.java | 77 +-- .../areashop/features/signs/RegionSign.java | 249 ++++++++++ .../signs/SignsFeature.java} | 363 ++++++++++++-- .../areashop/listeners/SignBreakListener.java | 121 ----- .../areashop/listeners/SignClickListener.java | 94 ---- .../areashop/managers/FeatureManager.java | 6 +- .../areashop/managers/SignLinkerManager.java | 9 +- .../areashop/regions/GeneralRegion.java | 8 +- 15 files changed, 653 insertions(+), 785 deletions(-) delete mode 100644 AreaShop/src/main/java/me/wiefferink/areashop/features/SignsFeature.java create mode 100644 AreaShop/src/main/java/me/wiefferink/areashop/features/signs/RegionSign.java rename AreaShop/src/main/java/me/wiefferink/areashop/{listeners/SignChangeListener.java => features/signs/SignsFeature.java} (50%) delete mode 100644 AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignBreakListener.java delete mode 100644 AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignClickListener.java diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/AreaShop.java b/AreaShop/src/main/java/me/wiefferink/areashop/AreaShop.java index 7c57c98..fbec2ab 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/AreaShop.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/AreaShop.java @@ -6,9 +6,6 @@ import me.wiefferink.areashop.interfaces.AreaShopInterface; import me.wiefferink.areashop.interfaces.WorldEditInterface; import me.wiefferink.areashop.interfaces.WorldGuardInterface; import me.wiefferink.areashop.listeners.PlayerLoginLogoutListener; -import me.wiefferink.areashop.listeners.SignBreakListener; -import me.wiefferink.areashop.listeners.SignChangeListener; -import me.wiefferink.areashop.listeners.SignClickListener; import me.wiefferink.areashop.managers.CommandManager; import me.wiefferink.areashop.managers.FeatureManager; import me.wiefferink.areashop.managers.FileManager; @@ -287,9 +284,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface { managers.add(featureManager); // Register the event listeners - getServer().getPluginManager().registerEvents(new SignChangeListener(this), this); - getServer().getPluginManager().registerEvents(new SignBreakListener(this), this); - getServer().getPluginManager().registerEvents(new SignClickListener(this), this); getServer().getPluginManager().registerEvents(new PlayerLoginLogoutListener(this), this); setupTasks(); diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/commands/AddsignCommand.java b/AreaShop/src/main/java/me/wiefferink/areashop/commands/AddsignCommand.java index 0b4d7ac..25f9a5c 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/commands/AddsignCommand.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/commands/AddsignCommand.java @@ -1,7 +1,8 @@ package me.wiefferink.areashop.commands; import com.sk89q.worldedit.bukkit.selections.CuboidSelection; -import me.wiefferink.areashop.features.SignsFeature; +import me.wiefferink.areashop.features.signs.RegionSign; +import me.wiefferink.areashop.features.signs.SignsFeature; import me.wiefferink.areashop.regions.GeneralRegion; import me.wiefferink.areashop.tools.Utils; import org.bukkit.Material; @@ -86,7 +87,7 @@ public class AddsignCommand extends CommandAreaShop { return; } } - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); + RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); if(regionSign != null) { plugin.message(sender, "addsign-alreadyRegistered", regionSign.getRegion()); return; diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/commands/DelsignCommand.java b/AreaShop/src/main/java/me/wiefferink/areashop/commands/DelsignCommand.java index b675ac4..70a0c6b 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/commands/DelsignCommand.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/commands/DelsignCommand.java @@ -1,6 +1,7 @@ package me.wiefferink.areashop.commands; -import me.wiefferink.areashop.features.SignsFeature; +import me.wiefferink.areashop.features.signs.RegionSign; +import me.wiefferink.areashop.features.signs.SignsFeature; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; @@ -50,7 +51,7 @@ public class DelsignCommand extends CommandAreaShop { plugin.message(sender, "delsign-noSign"); return; } - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); + RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); if(regionSign == null) { plugin.message(sender, "delsign-noRegion"); return; diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/commands/ImportJob.java b/AreaShop/src/main/java/me/wiefferink/areashop/commands/ImportJob.java index 710bb16..5b3ea83 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/commands/ImportJob.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/commands/ImportJob.java @@ -5,7 +5,8 @@ import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import javafx.util.Pair; import me.wiefferink.areashop.AreaShop; -import me.wiefferink.areashop.features.SignsFeature; +import me.wiefferink.areashop.features.signs.RegionSign; +import me.wiefferink.areashop.features.signs.SignsFeature; import me.wiefferink.areashop.regions.BuyRegion; import me.wiefferink.areashop.regions.GeneralRegion; import me.wiefferink.areashop.regions.RegionGroup; @@ -253,6 +254,7 @@ public class ImportJob { // Get existing owners and members List existing = new ArrayList<>(); if(owner != null) { + @SuppressWarnings("deprecation") OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner); if(offlinePlayer != null) { existing.add(offlinePlayer.getUniqueId()); @@ -387,7 +389,7 @@ public class ImportJob { } // Check if this location is already added to a region - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(location); + RegionSign regionSign = SignsFeature.getSignByLocation(location); if(regionSign != null) { if(!regionSign.getRegion().equals(region)) { message("import-signAlreadyAdded", region.getName(), signLocation, regionSign.getRegion().getName()); diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/features/FriendsFeature.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/FriendsFeature.java index dc04011..1b5c9eb 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/features/FriendsFeature.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/features/FriendsFeature.java @@ -16,7 +16,7 @@ import java.util.UUID; public class FriendsFeature extends RegionFeature { public FriendsFeature(GeneralRegion region) { - this.region = region; + setRegion(region); } /** @@ -27,17 +27,17 @@ public class FriendsFeature extends RegionFeature { */ public boolean addFriend(UUID player, CommandSender by) { // Fire and check event - AddedFriendEvent event = new AddedFriendEvent(region, Bukkit.getOfflinePlayer(player), by); + AddedFriendEvent event = new AddedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by); Bukkit.getPluginManager().callEvent(event); if(event.isCancelled()) { plugin.message(by, "general-cancelled", event.getReason(), this); return false; } - Set friends = new HashSet<>(region.getConfig().getStringList("general.friends")); + Set friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends")); friends.add(player.toString()); List list = new ArrayList<>(friends); - region.setSetting("general.friends", list); + getRegion().setSetting("general.friends", list); return true; } @@ -49,20 +49,20 @@ public class FriendsFeature extends RegionFeature { */ public boolean deleteFriend(UUID player, CommandSender by) { // Fire and check event - DeletedFriendEvent event = new DeletedFriendEvent(region, Bukkit.getOfflinePlayer(player), by); + DeletedFriendEvent event = new DeletedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by); Bukkit.getPluginManager().callEvent(event); if(event.isCancelled()) { plugin.message(by, "general-cancelled", event.getReason(), this); return false; } - Set friends = new HashSet<>(region.getConfig().getStringList("general.friends")); + Set friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends")); friends.remove(player.toString()); List list = new ArrayList<>(friends); if(list.isEmpty()) { - region.setSetting("general.friends", null); + getRegion().setSetting("general.friends", null); } else { - region.setSetting("general.friends", list); + getRegion().setSetting("general.friends", list); } return true; } @@ -73,7 +73,7 @@ public class FriendsFeature extends RegionFeature { */ public Set getFriends() { HashSet result = new HashSet<>(); - for(String friend : region.getConfig().getStringList("general.friends")) { + for(String friend : getRegion().getConfig().getStringList("general.friends")) { try { UUID id = UUID.fromString(friend); result.add(id); @@ -103,7 +103,7 @@ public class FriendsFeature extends RegionFeature { * Remove all friends that are added to this region. */ public void clearFriends() { - region.setSetting("general.friends", null); + getRegion().setSetting("general.friends", null); } } diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/features/RegionFeature.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/RegionFeature.java index 90c76c2..9f97bb6 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/features/RegionFeature.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/features/RegionFeature.java @@ -7,10 +7,26 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; public abstract class RegionFeature implements Listener { - static final AreaShop plugin = AreaShop.getInstance(); + public static final AreaShop plugin = AreaShop.getInstance(); public YamlConfiguration config = plugin.getConfig(); - GeneralRegion region; + private GeneralRegion region; + + /** + * Set the region for this feature. + * @param region Feature region + */ + public void setRegion(GeneralRegion region) { + this.region = region; + } + + /** + * Get the region of this feature. + * @return region of this feature, or null if generic + */ + public GeneralRegion getRegion() { + return region; + } /** * Start listening to events. diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/features/SignsFeature.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/SignsFeature.java deleted file mode 100644 index 602c9d0..0000000 --- a/AreaShop/src/main/java/me/wiefferink/areashop/features/SignsFeature.java +++ /dev/null @@ -1,449 +0,0 @@ -package me.wiefferink.areashop.features; - -import me.wiefferink.areashop.AreaShop; -import me.wiefferink.areashop.events.notify.UpdateRegionEvent; -import me.wiefferink.areashop.regions.GeneralRegion; -import me.wiefferink.areashop.tools.Utils; -import me.wiefferink.bukkitdo.Do; -import me.wiefferink.interactivemessenger.processing.Message; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Sign; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class SignsFeature extends RegionFeature { - - private static Map allSigns = Collections.synchronizedMap(new HashMap<>()); - private static Map> signsByChunk = Collections.synchronizedMap(new HashMap<>()); - - private Map signs; - - public SignsFeature() { - - } - - /** - * Constructor. - * @param region The region to bind to - */ - public SignsFeature(GeneralRegion region) { - this.region = region; - signs = new HashMap<>(); - // Setup current signs - ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs"); - if(signSection != null) { - for(String signKey : signSection.getKeys(false)) { - RegionSign sign = new RegionSign(region, signKey); - Location location = sign.getLocation(); - if(location == null) { - AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location"); - continue; - } - signs.put(sign.getStringLocation(), sign); - signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>()) - .add(sign); - } - allSigns.putAll(signs); - } - } - - @Override - public void shutdown() { - // Deregister signs from the registry - if(signs != null) { - for(String key : signs.keySet()) { - allSigns.remove(key); - } - } - } - - /** - * Convert a location to a string to use as map key. - * @param location The location to get the key for - * @return A string to use in a map for a location - */ - private static String locationToString(Location location) { - return location.getWorld().getName() + ";" + location.getBlockX() + ";" + location.getBlockY() + ";" + location.getBlockZ(); - } - - /** - * Convert a chunk to a string to use as map key. - * @param location The location to get the key for - * @return A string to use in a map for a chunk - */ - private static String chunkToString(Location location) { - return location.getWorld().getName() + ";" + (location.getBlockX() >> 4) + ";" + (location.getBlockZ() >> 4); - } - - /** - * Convert a chunk to a string to use as map key. - * Use a Location argument to prevent chunk loading! - * @param chunk The location to get the key for - * @return A string to use in a map for a chunk - */ - private static String chunkToString(Chunk chunk) { - return chunk.getWorld().getName() + ";" + chunk.getX() + ";" + chunk.getZ(); - } - - /** - * Get a sign by a location. - * @param location The location to get the sign for - * @return The RegionSign that is at the location, or null if none - */ - public static RegionSign getSignByLocation(Location location) { - return allSigns.get(locationToString(location)); - } - - @EventHandler - public void regionUpdate(UpdateRegionEvent event) { - event.getRegion().getSignsFeature().update(); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onChunkLoad(ChunkLoadEvent event) { - List chunkSigns = signsByChunk.get(chunkToString(event.getChunk())); - if(chunkSigns == null) { - return; - } - - Do.forAll(chunkSigns, RegionSign::update); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onChunkUnload(ChunkUnloadEvent event) { - List chunkSigns = signsByChunk.get(chunkToString(event.getChunk())); - } - - /** - * Update all signs connected to this region. - * @return true if all signs are updated correctly, false if one or more updates failed - */ - public boolean update() { - boolean result = true; - for(RegionSign sign : signs.values()) { - result = result & sign.update(); - } - return result; - } - - /** - * Check if any of the signs need periodic updating. - * @return true if one or more of the signs need periodic updating, otherwise false - */ - public boolean needsPeriodicUpdate() { - boolean result = false; - for(RegionSign sign : signs.values()) { - result = result | sign.needsPeriodicUpdate(); - } - return result; - } - - /** - * Get the signs of this region. - * @return List of signs - */ - public List getSigns() { - return Collections.unmodifiableList(new ArrayList<>(signs.values())); - } - - /** - * Get a list with all sign locations. - * @return A List with all sign locations - */ - public List getSignLocations() { - List result = new ArrayList<>(); - for(RegionSign sign : signs.values()) { - result.add(sign.getLocation()); - } - return result; - } - - /** - * Add a sign to this region. - * @param location The location of the sign - * @param signType The type of the sign (WALL_SIGN or SIGN_POST) - * @param facing The orientation of the sign - * @param profile The profile to use with this sign (null for default) - */ - public void addSign(Location location, Material signType, BlockFace facing, String profile) { - int i = 0; - while(region.getConfig().isSet("general.signs." + i)) { - i++; - } - String signPath = "general.signs." + i + "."; - region.setSetting(signPath + "location", Utils.locationToConfig(location)); - region.setSetting(signPath + "facing", facing != null ? facing.name() : null); - region.setSetting(signPath + "signType", signType != null ? signType.name() : null); - if(profile != null && profile.length() != 0) { - region.setSetting(signPath + "profile", profile); - } - // Add to the map - RegionSign sign = new RegionSign(region, i + ""); - signs.put(sign.getStringLocation(), sign); - allSigns.put(sign.getStringLocation(), sign); - signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>()) - .add(sign); - } - - /** - * Checks if there is a sign from this region at the specified location. - * @param location Location to check - * @return true if this region has a sign at the location, otherwise false - */ - public boolean isSignOfRegion(Location location) { - Set signs; - if(region.getConfig().getConfigurationSection("general.signs") == null) { - return false; - } - signs = region.getConfig().getConfigurationSection("general.signs").getKeys(false); - for(String sign : signs) { - Location signLocation = Utils.configToLocation(region.getConfig().getConfigurationSection("general.signs." + sign + ".location")); - if(signLocation != null - && signLocation.getWorld().equals(location.getWorld()) - && signLocation.getBlockX() == location.getBlockX() - && signLocation.getBlockY() == location.getBlockY() - && signLocation.getBlockZ() == location.getBlockZ()) { - return true; - } - } - return false; - } - - - /** - * Sign that is connected to a region to display information and interact with the region. - */ - public class RegionSign { - - private GeneralRegion region; - private String key; - - public RegionSign(GeneralRegion region, String key) { - this.region = region; - this.key = key; - } - - /** - * Get the location of this sign. - * @return The location of this sign - */ - public Location getLocation() { - return Utils.configToLocation(region.getConfig().getConfigurationSection("general.signs." + key + ".location")); - } - - /** - * Location string to be used as key in maps. - * @return Location string - */ - public String getStringLocation() { - return locationToString(getLocation()); - } - - /** - * Chunk string to be used as key in maps. - * @return Chunk string - */ - public String getStringChunk() { - return chunkToString(getLocation()); - } - - /** - * Get the region this sign is linked to. - * @return The region this sign is linked to - */ - public GeneralRegion getRegion() { - return region; - } - - /** - * Remove this sign from the region. - */ - public void remove() { - getLocation().getBlock().setType(Material.AIR); - signs.remove(getStringLocation()); - allSigns.remove(getStringLocation()); - region.setSetting("general.signs." + key, null); - } - - /** - * Get the ConfigurationSection defining the sign layout. - * @return The sign layout config - */ - public ConfigurationSection getProfile() { - return region.getConfigurationSectionSetting("general.signProfile", "signProfiles", region.getConfig().get("general.signs." + key + ".profile")); - } - - /** - * Get the facing of the sign. - * @return BlockFace the sign faces, or null if unknown - */ - public BlockFace getFacing() { - try { - return BlockFace.valueOf(region.getConfig().getString("general.signs." + key + ".facing")); - } catch(NullPointerException | IllegalArgumentException e) { - return null; - } - } - - /** - * Get the material of the sign - * @return Material of the sign, normally {@link Material#WALL_SIGN} or {@link Material#SIGN_POST}, but could be something else or null. - */ - public Material getMaterial() { - try { - return Material.valueOf(region.getConfig().getString("general.signs." + key + ".signType")); - } catch(NullPointerException | IllegalArgumentException e) { - return null; - } - } - - /** - * Update this sign. - * @return true if the update was successful, otherwise false - */ - public boolean update() { - // Ignore updates of signs in chunks that are not loaded - Location signLocation = getLocation(); - if(!signLocation.getWorld().isChunkLoaded(signLocation.getBlockX() >> 4, signLocation.getBlockZ() >> 4)) { - return false; - } - - if(region.isDeleted()) { - return false; - } - - YamlConfiguration regionConfig = region.getConfig(); - ConfigurationSection signConfig = getProfile(); - Block block = signLocation.getBlock(); - if(signConfig == null || !signConfig.isSet(region.getState().getValue())) { - block.setType(Material.AIR); - return true; - } - - ConfigurationSection stateConfig = signConfig.getConfigurationSection(region.getState().getValue()); - - // Get the lines - String[] signLines = new String[4]; - boolean signEmpty = true; - for(int i = 0; i < 4; i++) { - signLines[i] = stateConfig.getString("line" + (i + 1)); - signEmpty &= (signLines[i] == null || signLines[i].isEmpty()); - } - if(signEmpty) { - block.setType(Material.AIR); - return true; - } - - Sign signState = null; - // Place the sign back (with proper rotation and type) after it has been hidden or (indirectly) destroyed - if(block.getType() != Material.WALL_SIGN && block.getType() != Material.SIGN_POST) { - Material signType = getMaterial(); - if(signType != Material.WALL_SIGN && signType != Material.SIGN_POST) { - AreaShop.debug("Setting sign", key, "of region", region.getName(), "failed, could not set sign block back"); - return false; - } - block.setType(signType); - signState = (Sign)block.getState(); - org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData(); - BlockFace signFace = getFacing(); - if(signFace != null) { - signData.setFacingDirection(signFace); - signState.setData(signData); - } - } - if(signState == null) { - signState = (Sign)block.getState(); - } - - // Save current rotation and type - org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData(); - if(!regionConfig.isString("general.signs." + key + ".signType")) { - region.setSetting("general.signs." + key + ".signType", signState.getType().toString()); - } - if(!regionConfig.isString("general.signs." + key + ".facing")) { - region.setSetting("general.signs." + key + ".facing", signData.getFacing().toString()); - } - - // Apply replacements and color and then set it on the sign - for(int i = 0; i < signLines.length; i++) { - if(signLines[i] == null) { - signState.setLine(i, ""); - continue; - } - signLines[i] = Message.fromString(signLines[i]).replacements(region).getSingle(); - signLines[i] = Utils.applyColors(signLines[i]); - signState.setLine(i, signLines[i]); - } - signState.update(); - return true; - } - - /** - * Check if the sign needs to update periodically. - * @return true if it needs periodic updates, otherwise false - */ - public boolean needsPeriodicUpdate() { - ConfigurationSection signConfig = getProfile(); - if(signConfig == null || !signConfig.isSet(region.getState().getValue().toLowerCase())) { - return false; - } - ConfigurationSection stateConfig = signConfig.getConfigurationSection(region.getState().getValue().toLowerCase()); - // Check the lines for the timeleft tag - for(int i = 1; i <= 4; i++) { - String line = stateConfig.getString("line" + i); - if(line != null && !line.isEmpty() && line.contains(Message.VARIABLE_START + AreaShop.tagTimeLeft + Message.VARIABLE_END)) { - return true; - } - } - return false; - } - - /** - * Run commands when a player clicks a sign. - * @param clicker The player that clicked the sign - * @param clickType The type of clicking - * @return true if the commands ran successfully, false if any of them failed - */ - public boolean runSignCommands(Player clicker, GeneralRegion.ClickType clickType) { - ConfigurationSection signConfig = getProfile(); - if(signConfig == null) { - return false; - } - ConfigurationSection stateConfig = signConfig.getConfigurationSection(region.getState().getValue().toLowerCase()); - - // Run player commands if specified - List playerCommands = new ArrayList<>(); - for(String command : stateConfig.getStringList(clickType.getValue() + "Player")) { - // TODO move variable checking code to InteractiveMessenger? - playerCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName())); - } - region.runCommands(clicker, playerCommands); - - // Run console commands if specified - List consoleCommands = new ArrayList<>(); - for(String command : stateConfig.getStringList(clickType.getValue() + "Console")) { - consoleCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName())); - } - region.runCommands(Bukkit.getConsoleSender(), consoleCommands); - - return !playerCommands.isEmpty() || !consoleCommands.isEmpty(); - } - } -} diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/features/TeleportFeature.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/TeleportFeature.java index 876b06c..43035e4 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/features/TeleportFeature.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/features/TeleportFeature.java @@ -2,6 +2,7 @@ package me.wiefferink.areashop.features; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import me.wiefferink.areashop.AreaShop; +import me.wiefferink.areashop.features.signs.RegionSign; import me.wiefferink.areashop.regions.GeneralRegion; import me.wiefferink.areashop.tools.Utils; import me.wiefferink.areashop.tools.Value; @@ -29,7 +30,7 @@ public class TeleportFeature extends RegionFeature { public TeleportFeature(GeneralRegion region) { - this.region = region; + setRegion(region); } /** @@ -37,7 +38,7 @@ public class TeleportFeature extends RegionFeature { * @return The teleport location, or null if not set */ public Location getTeleportLocation() { - return Utils.configToLocation(region.getConfigurationSectionSetting("general.teleportLocation")); + return Utils.configToLocation(getRegion().getConfigurationSectionSetting("general.teleportLocation")); } /** @@ -45,7 +46,7 @@ public class TeleportFeature extends RegionFeature { * @return true if the region has a teleportlocation, false otherwise */ public boolean hasTeleportLocation() { - return region.getConfigurationSectionSetting("general.teleportLocation") != null; + return getRegion().getConfigurationSectionSetting("general.teleportLocation") != null; } /** @@ -54,9 +55,9 @@ public class TeleportFeature extends RegionFeature { */ public void setTeleport(Location location) { if(location == null) { - region.setSetting("general.teleportLocation", null); + getRegion().setSetting("general.teleportLocation", null); } else { - region.setSetting("general.teleportLocation", Utils.locationToConfig(location, true)); + getRegion().setSetting("general.teleportLocation", Utils.locationToConfig(location, true)); } } @@ -70,57 +71,57 @@ public class TeleportFeature extends RegionFeature { public boolean teleportPlayer(Player player, boolean toSign, boolean checkRestrictions) { // Check basics - if(region.getWorld() == null) { - region.message(player, "general-noWorld"); + if(getRegion().getWorld() == null) { + getRegion().message(player, "general-noWorld"); return false; } - if(region.getRegion() == null) { - region.message(player, "general-noRegion"); + if(getRegion().getRegion() == null) { + getRegion().message(player, "general-noRegion"); return false; } if(checkRestrictions) { // Check correct world - if(!region.getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(region.getWorld())) { - region.message(player, "teleport-wrongWorld", player.getWorld().getName()); + if(!getRegion().getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(getRegion().getWorld())) { + getRegion().message(player, "teleport-wrongWorld", player.getWorld().getName()); return false; } - boolean owner = player.getUniqueId().equals(region.getOwner()); - boolean friend = region.getFriendsFeature().getFriends().contains(player.getUniqueId()); - boolean available = region.isAvailable(); + boolean owner = player.getUniqueId().equals(getRegion().getOwner()); + boolean friend = getRegion().getFriendsFeature().getFriends().contains(player.getUniqueId()); + boolean available = getRegion().isAvailable(); // Teleport to sign instead if they dont have permission for teleporting to region if((!toSign && owner && !player.hasPermission("areashop.teleport") && player.hasPermission("areashop.teleportsign") || !toSign && !owner && !friend && !player.hasPermission("areashop.teleportall") && player.hasPermission("areashop.teleportsignall") || !toSign && !owner && friend && !player.hasPermission("areashop.teleportfriend") && player.hasPermission("areashop.teleportfriendsign") || !toSign && !owner && !friend && available && !player.hasPermission("areashop.teleportavailable") && player.hasPermission("areashop.teleportavailablesign"))) { - region.message(player, "teleport-changedToSign"); + getRegion().message(player, "teleport-changedToSign"); toSign = true; } // Check permissions if(owner && !available && !player.hasPermission("areashop.teleport") && !toSign) { - region.message(player, "teleport-noPermission"); + getRegion().message(player, "teleport-noPermission"); return false; } else if(!owner && !available && !player.hasPermission("areashop.teleportall") && !toSign && !friend) { - region.message(player, "teleport-noPermissionOther"); + getRegion().message(player, "teleport-noPermissionOther"); return false; } else if(!owner && !available && !player.hasPermission("areashop.teleportfriend") && !toSign && friend) { - region.message(player, "teleport-noPermissionFriend"); + getRegion().message(player, "teleport-noPermissionFriend"); return false; } else if(available && !player.hasPermission("areashop.teleportavailable") && !toSign) { - region.message(player, "teleport-noPermissionAvailable"); + getRegion().message(player, "teleport-noPermissionAvailable"); return false; } else if(owner && !available && !player.hasPermission("areashop.teleportsign") && toSign) { - region.message(player, "teleport-noPermissionSign"); + getRegion().message(player, "teleport-noPermissionSign"); return false; } else if(!owner && !available && !player.hasPermission("areashop.teleportsignall") && toSign && !friend) { - region.message(player, "teleport-noPermissionOtherSign"); + getRegion().message(player, "teleport-noPermissionOtherSign"); return false; } else if(!owner && !available && !player.hasPermission("areashop.teleportfriendsign") && toSign && friend) { - region.message(player, "teleport-noPermissionFriendSign"); + getRegion().message(player, "teleport-noPermissionFriendSign"); return false; } else if(available && !player.hasPermission("areashop.teleportavailablesign") && toSign) { - region.message(player, "teleport-noPermissionAvailableSign"); + getRegion().message(player, "teleport-noPermissionAvailableSign"); return false; } } @@ -132,18 +133,18 @@ public class TeleportFeature extends RegionFeature { boolean insideRegion; if(toSign) { - insideRegion = region.getBooleanSetting("general.teleportToSignIntoRegion"); + insideRegion = getRegion().getBooleanSetting("general.teleportToSignIntoRegion"); } else { - insideRegion = region.getBooleanSetting("general.teleportIntoRegion"); + insideRegion = getRegion().getBooleanSetting("general.teleportIntoRegion"); } // Check locations starting from startLocation and then a cube that increases // radius around that (until no block in the region is found at all cube sides) Location safeLocation = startLocation; - ProtectedRegion worldguardRegion = region.getRegion(); + ProtectedRegion worldguardRegion = getRegion().getRegion(); boolean blocksInRegion = worldguardRegion.contains(startLocation.getBlockX(), startLocation.getBlockY(), startLocation.getBlockZ()); if(!blocksInRegion && insideRegion) { - region.message(player, "teleport-blocked"); + getRegion().message(player, "teleport-blocked"); return false; } @@ -355,24 +356,24 @@ public class TeleportFeature extends RegionFeature { } if(done && isSafe(safeLocation)) { if(toSign) { - region.message(player, "teleport-successSign"); + getRegion().message(player, "teleport-successSign"); // Let the player look at the sign Vector playerVector = safeLocation.toVector(); playerVector.setY(playerVector.getY() + player.getEyeHeight(true)); - Vector signVector = region.getSignsFeature().getSigns().get(0).getLocation().toVector().add(new Vector(0.5, 0.5, 0.5)); + Vector signVector = getRegion().getSignsFeature().getSigns().get(0).getLocation().toVector().add(new Vector(0.5, 0.5, 0.5)); Vector direction = playerVector.clone().subtract(signVector).normalize(); safeLocation.setYaw(180 - (float)Math.toDegrees(Math.atan2(direction.getX(), direction.getZ()))); safeLocation.setPitch(90 - (float)Math.toDegrees(Math.acos(direction.getY()))); } else { - region.message(player, "teleport-success"); + getRegion().message(player, "teleport-success"); } player.teleport(safeLocation); AreaShop.debug("Found location: " + safeLocation.toString() + " Tries: " + (checked - 1)); return true; } else { - region.message(player, "teleport-noSafe", checked - 1, maxTries); + getRegion().message(player, "teleport-noSafe", checked - 1, maxTries); AreaShop.debug("No location found, checked " + (checked - 1) + " spots of max " + maxTries); return false; } @@ -451,10 +452,10 @@ public class TeleportFeature extends RegionFeature { */ private Location getStartLocation(Player player, Value toSign) { Location startLocation = null; - ProtectedRegion worldguardRegion = region.getRegion(); + ProtectedRegion worldguardRegion = getRegion().getRegion(); // Try to get sign location - List signs = region.getSignsFeature().getSigns(); + List signs = getRegion().getSignsFeature().getSigns(); boolean signAvailable = !signs.isEmpty(); if(toSign.get()) { if(signAvailable) { @@ -464,9 +465,9 @@ public class TeleportFeature extends RegionFeature { startLocation.setYaw(player.getLocation().getYaw()); // Move player x blocks away from the sign - double distance = region.getDoubleSetting("general.teleportSignDistance"); + double distance = getRegion().getDoubleSetting("general.teleportSignDistance"); if(distance > 0) { - BlockFace facing = region.getSignsFeature().getSigns().get(0).getFacing(); + BlockFace facing = getRegion().getSignsFeature().getSigns().get(0).getFacing(); Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ()) .normalize() .multiply(distance); @@ -476,7 +477,7 @@ public class TeleportFeature extends RegionFeature { } } else { // No sign available - region.message(player, "teleport-changedToNoSign"); + getRegion().message(player, "teleport-changedToNoSign"); toSign.set(false); } } @@ -490,7 +491,7 @@ public class TeleportFeature extends RegionFeature { if(startLocation == null) { // Set to block in the middle, y configured in the config com.sk89q.worldedit.Vector middle = com.sk89q.worldedit.Vector.getMidpoint(worldguardRegion.getMaximumPoint(), worldguardRegion.getMinimumPoint()); - String configSetting = region.getStringSetting("general.teleportLocationY"); + String configSetting = getRegion().getStringSetting("general.teleportLocationY"); if("bottom".equalsIgnoreCase(configSetting)) { middle = middle.setY(worldguardRegion.getMinimumPoint().getBlockY()); } else if("top".equalsIgnoreCase(configSetting)) { @@ -505,7 +506,7 @@ public class TeleportFeature extends RegionFeature { AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'"); } } - startLocation = new Location(region.getWorld(), middle.getX(), middle.getY(), middle.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch()); + startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch()); } // Set location in the center of the block diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/features/signs/RegionSign.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/signs/RegionSign.java new file mode 100644 index 0000000..dc19658 --- /dev/null +++ b/AreaShop/src/main/java/me/wiefferink/areashop/features/signs/RegionSign.java @@ -0,0 +1,249 @@ +package me.wiefferink.areashop.features.signs; + +import me.wiefferink.areashop.AreaShop; +import me.wiefferink.areashop.regions.GeneralRegion; +import me.wiefferink.areashop.tools.Utils; +import me.wiefferink.interactivemessenger.processing.Message; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Sign; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sign that is connected to a region to display information and interact with the region. + */ +public class RegionSign { + + private SignsFeature signsFeature; + private String key; + + public RegionSign(SignsFeature signsFeature, String key) { + this.signsFeature = signsFeature; + this.key = key; + } + + /** + * Get the location of this sign. + * @return The location of this sign + */ + public Location getLocation() { + return Utils.configToLocation(getRegion().getConfig().getConfigurationSection("general.signs." + key + ".location")); + } + + /** + * Location string to be used as key in maps. + * @return Location string + */ + public String getStringLocation() { + return SignsFeature.locationToString(getLocation()); + } + + /** + * Chunk string to be used as key in maps. + * @return Chunk string + */ + public String getStringChunk() { + return SignsFeature.chunkToString(getLocation()); + } + + /** + * Get the region this sign is linked to. + * @return The region this sign is linked to + */ + public GeneralRegion getRegion() { + return signsFeature.getRegion(); + } + + /** + * Remove this sign from the region. + */ + public void remove() { + getLocation().getBlock().setType(Material.AIR); + signsFeature.getSignsRef().remove(getStringLocation()); + SignsFeature.getAllSigns().remove(getStringLocation()); + SignsFeature.getSignsByChunk().get(getStringChunk()).remove(this); + getRegion().setSetting("general.signs." + key, null); + } + + /** + * Get the ConfigurationSection defining the sign layout. + * @return The sign layout config + */ + public ConfigurationSection getProfile() { + return getRegion().getConfigurationSectionSetting("general.signProfile", "signProfiles", getRegion().getConfig().get("general.signs." + key + ".profile")); + } + + /** + * Get the facing of the sign. + * @return BlockFace the sign faces, or null if unknown + */ + public BlockFace getFacing() { + try { + return BlockFace.valueOf(getRegion().getConfig().getString("general.signs." + key + ".facing")); + } catch(NullPointerException | IllegalArgumentException e) { + return null; + } + } + + /** + * Get the material of the sign + * @return Material of the sign, normally {@link Material#WALL_SIGN} or {@link Material#SIGN_POST}, but could be something else or null. + */ + public Material getMaterial() { + try { + return Material.valueOf(getRegion().getConfig().getString("general.signs." + key + ".signType")); + } catch(NullPointerException | IllegalArgumentException e) { + return null; + } + } + + /** + * Update this sign. + * @return true if the update was successful, otherwise false + */ + public boolean update() { + // Ignore updates of signs in chunks that are not loaded + Location signLocation = getLocation(); + if(!signLocation.getWorld().isChunkLoaded(signLocation.getBlockX() >> 4, signLocation.getBlockZ() >> 4)) { + return false; + } + + if(getRegion().isDeleted()) { + return false; + } + + YamlConfiguration regionConfig = getRegion().getConfig(); + ConfigurationSection signConfig = getProfile(); + Block block = signLocation.getBlock(); + if(signConfig == null || !signConfig.isSet(getRegion().getState().getValue())) { + block.setType(Material.AIR); + return true; + } + + ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue()); + + // Get the lines + String[] signLines = new String[4]; + boolean signEmpty = true; + for(int i = 0; i < 4; i++) { + signLines[i] = stateConfig.getString("line" + (i + 1)); + signEmpty &= (signLines[i] == null || signLines[i].isEmpty()); + } + if(signEmpty) { + block.setType(Material.AIR); + return true; + } + + Sign signState = null; + // Place the sign back (with proper rotation and type) after it has been hidden or (indirectly) destroyed + if(block.getType() != Material.WALL_SIGN && block.getType() != Material.SIGN_POST) { + Material signType = getMaterial(); + if(signType != Material.WALL_SIGN && signType != Material.SIGN_POST) { + AreaShop.debug("Setting sign", key, "of region", getRegion().getName(), "failed, could not set sign block back"); + return false; + } + block.setType(signType); + signState = (Sign)block.getState(); + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData(); + BlockFace signFace = getFacing(); + if(signFace != null) { + signData.setFacingDirection(signFace); + signState.setData(signData); + } + } + if(signState == null) { + signState = (Sign)block.getState(); + } + + // Save current rotation and type + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData(); + if(!regionConfig.isString("general.signs." + key + ".signType")) { + getRegion().setSetting("general.signs." + key + ".signType", signState.getType().toString()); + } + if(!regionConfig.isString("general.signs." + key + ".facing")) { + getRegion().setSetting("general.signs." + key + ".facing", signData.getFacing().toString()); + } + + // Apply replacements and color and then set it on the sign + for(int i = 0; i < signLines.length; i++) { + if(signLines[i] == null) { + signState.setLine(i, ""); + continue; + } + signLines[i] = Message.fromString(signLines[i]).replacements(getRegion()).getSingle(); + signLines[i] = Utils.applyColors(signLines[i]); + signState.setLine(i, signLines[i]); + } + signState.update(); + return true; + } + + /** + * Check if the sign needs to update periodically. + * @return true if it needs periodic updates, otherwise false + */ + public boolean needsPeriodicUpdate() { + ConfigurationSection signConfig = getProfile(); + if(signConfig == null || !signConfig.isSet(getRegion().getState().getValue().toLowerCase())) { + return false; + } + ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase()); + // Check the lines for the timeleft tag + for(int i = 1; i <= 4; i++) { + String line = stateConfig.getString("line" + i); + if(line != null && !line.isEmpty() && line.contains(Message.VARIABLE_START + AreaShop.tagTimeLeft + Message.VARIABLE_END)) { + return true; + } + } + return false; + } + + /** + * Run commands when a player clicks a sign. + * @param clicker The player that clicked the sign + * @param clickType The type of clicking + * @return true if the commands ran successfully, false if any of them failed + */ + public boolean runSignCommands(Player clicker, GeneralRegion.ClickType clickType) { + ConfigurationSection signConfig = getProfile(); + if(signConfig == null) { + return false; + } + ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase()); + + // Run player commands if specified + List playerCommands = new ArrayList<>(); + for(String command : stateConfig.getStringList(clickType.getValue() + "Player")) { + // TODO move variable checking code to InteractiveMessenger? + playerCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName())); + } + getRegion().runCommands(clicker, playerCommands); + + // Run console commands if specified + List consoleCommands = new ArrayList<>(); + for(String command : stateConfig.getStringList(clickType.getValue() + "Console")) { + consoleCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName())); + } + getRegion().runCommands(Bukkit.getConsoleSender(), consoleCommands); + + return !playerCommands.isEmpty() || !consoleCommands.isEmpty(); + } + + @Override + public boolean equals(Object object) { + return object instanceof RegionSign && ((RegionSign)object).getRegion().equals(this.getRegion()) && ((RegionSign)object).key.equals(this.key); + } + + @Override + public int hashCode() { + return key.hashCode(); + } +} diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignChangeListener.java b/AreaShop/src/main/java/me/wiefferink/areashop/features/signs/SignsFeature.java similarity index 50% rename from AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignChangeListener.java rename to AreaShop/src/main/java/me/wiefferink/areashop/features/signs/SignsFeature.java index 003c4e6..2938638 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignChangeListener.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/features/signs/SignsFeature.java @@ -1,43 +1,159 @@ -package me.wiefferink.areashop.listeners; +package me.wiefferink.areashop.features.signs; import com.sk89q.worldedit.bukkit.selections.CuboidSelection; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import me.wiefferink.areashop.AreaShop; +import me.wiefferink.areashop.events.notify.UpdateRegionEvent; +import me.wiefferink.areashop.features.RegionFeature; import me.wiefferink.areashop.managers.FileManager; import me.wiefferink.areashop.regions.BuyRegion; import me.wiefferink.areashop.regions.GeneralRegion; import me.wiefferink.areashop.regions.RentRegion; import me.wiefferink.areashop.tools.Utils; import me.wiefferink.bukkitdo.Do; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.material.Sign; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; -/** - * Checks for placement of signs. - */ -public final class SignChangeListener implements Listener { - private AreaShop plugin; +public class SignsFeature extends RegionFeature { + + private static Map allSigns = Collections.synchronizedMap(new HashMap<>()); + private static Map> signsByChunk = Collections.synchronizedMap(new HashMap<>()); + + private Map signs; + + public SignsFeature() { - /** - * Constructor. - * @param plugin The AreaShop plugin - */ - public SignChangeListener(AreaShop plugin) { - this.plugin = plugin; } /** - * Called when a sign is changed. - * @param event The event + * Constructor. + * @param region The region to bind to */ + public SignsFeature(GeneralRegion region) { + setRegion(region); + signs = new HashMap<>(); + // Setup current signs + ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs"); + if(signSection != null) { + for(String signKey : signSection.getKeys(false)) { + RegionSign sign = new RegionSign(this, signKey); + Location location = sign.getLocation(); + if(location == null) { + AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location"); + continue; + } + signs.put(sign.getStringLocation(), sign); + signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>()) + .add(sign); + } + allSigns.putAll(signs); + } + } + + @Override + public void shutdown() { + // Deregister signs from the registry + if(signs != null) { + for(Map.Entry entry : signs.entrySet()) { + allSigns.remove(entry.getKey()); + signsByChunk.get(entry.getValue().getStringChunk()).remove(entry.getValue()); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onSignBreak(BlockBreakEvent event) { + if(event.isCancelled()) { + return; + } + Block block = event.getBlock(); + // Check if it is a sign + if(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST) { + // Check if the rent sign is really the same as a saved rent + RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); + if(regionSign == null) { + return; + } + // Remove the sign of the rental region if the player has permission + if(event.getPlayer().hasPermission("areashop.delsign")) { + regionSign.remove(); + plugin.message(event.getPlayer(), "delsign-success", regionSign.getRegion()); + } else { // Cancel the breaking of the sign + event.setCancelled(true); + plugin.message(event.getPlayer(), "delsign-noPermission", regionSign.getRegion()); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onIndirectSignBreak(BlockPhysicsEvent event) { + if(event.getBlock().getType() == Material.SIGN_POST || event.getBlock().getType() == Material.WALL_SIGN) { + // Check if the rent sign is really the same as a saved rent + if(SignsFeature.getSignByLocation(event.getBlock().getLocation()) != null) { + // Cancel the sign breaking, will create a floating sign but at least it is not disconnected/gone + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onSignClick(PlayerInteractEvent event) { + if(event.isCancelled()) { + return; + } + Block block = event.getClickedBlock(); + // Check for clicking a sign and rightclicking + if((event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK) + && (block.getType() == Material.SIGN_POST || block.getType() == Material.WALL_SIGN)) { + // Check if the rent sign is really the same as a saved rent + RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); + if(regionSign == null) { + return; + } + Player player = event.getPlayer(); + if(plugin.getSignlinkerManager().isInSignLinkMode(player)) { + return; + } + // Get the clicktype + GeneralRegion.ClickType clickType = null; + if(player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) { + clickType = GeneralRegion.ClickType.SHIFTLEFTCLICK; + } else if(!player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) { + clickType = GeneralRegion.ClickType.LEFTCLICK; + } else if(player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + clickType = GeneralRegion.ClickType.SHIFTRIGHTCLICK; + } else if(!player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + clickType = GeneralRegion.ClickType.RIGHTCLICK; + } + // Run the commands + boolean ran = regionSign.runSignCommands(player, clickType); + // Only cancel event if at least one command has been executed + event.setCancelled(ran); + } + } + @EventHandler(priority = EventPriority.MONITOR) public void onSignChange(SignChangeEvent event) { if(event.isCancelled()) { @@ -146,7 +262,7 @@ public final class SignChangeListener implements Listener { if(durationSet) { rent.setDuration(thirdLine); } - Sign sign = (Sign)event.getBlock().getState().getData(); + org.bukkit.material.Sign sign = (org.bukkit.material.Sign)event.getBlock().getState().getData(); rent.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null); // Run commands @@ -249,7 +365,7 @@ public final class SignChangeListener implements Listener { if(priceSet) { buy.setPrice(price); } - Sign sign = (Sign)event.getBlock().getState().getData(); + org.bukkit.material.Sign sign = (org.bukkit.material.Sign)event.getBlock().getState().getData(); buy.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null); // Run commands buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, true); @@ -294,7 +410,7 @@ public final class SignChangeListener implements Listener { } region = regions.get(0); } - Sign sign = (Sign)event.getBlock().getState().getData(); + org.bukkit.material.Sign sign = (org.bukkit.material.Sign)event.getBlock().getState().getData(); if(thirdLine == null || thirdLine.length() == 0) { region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null); plugin.message(player, "addsign-success", region); @@ -307,36 +423,181 @@ public final class SignChangeListener implements Listener { Do.sync(region::update); } } + + /** + * Convert a location to a string to use as map key. + * @param location The location to get the key for + * @return A string to use in a map for a location + */ + public static String locationToString(Location location) { + return location.getWorld().getName() + ";" + location.getBlockX() + ";" + location.getBlockY() + ";" + location.getBlockZ(); + } + + /** + * Convert a chunk to a string to use as map key. + * @param location The location to get the key for + * @return A string to use in a map for a chunk + */ + public static String chunkToString(Location location) { + return location.getWorld().getName() + ";" + (location.getBlockX() >> 4) + ";" + (location.getBlockZ() >> 4); + } + + /** + * Convert a chunk to a string to use as map key. + * Use a Location argument to prevent chunk loading! + * @param chunk The location to get the key for + * @return A string to use in a map for a chunk + */ + public static String chunkToString(Chunk chunk) { + return chunk.getWorld().getName() + ";" + chunk.getX() + ";" + chunk.getZ(); + } + + /** + * Get a sign by a location. + * @param location The location to get the sign for + * @return The RegionSign that is at the location, or null if none + */ + public static RegionSign getSignByLocation(Location location) { + return allSigns.get(locationToString(location)); + } + + /** + * Get the map with all signs. + * @return Map with all signs: locationString -> RegionSign + */ + public static Map getAllSigns() { + return allSigns; + } + + /** + * Get the map with signs by chunk. + * @return Map with signs by chunk: chunkString -> List of RegionSign + */ + public static Map> getSignsByChunk() { + return signsByChunk; + } + + @EventHandler + public void regionUpdate(UpdateRegionEvent event) { + event.getRegion().getSignsFeature().update(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onChunkLoad(ChunkLoadEvent event) { + List chunkSigns = signsByChunk.get(chunkToString(event.getChunk())); + if(chunkSigns == null) { + return; + } + + Do.forAll(chunkSigns, RegionSign::update); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onChunkUnload(ChunkUnloadEvent event) { + List chunkSigns = signsByChunk.get(chunkToString(event.getChunk())); + } + + /** + * Update all signs connected to this region. + * @return true if all signs are updated correctly, false if one or more updates failed + */ + public boolean update() { + boolean result = true; + for(RegionSign sign : signs.values()) { + result = result & sign.update(); + } + return result; + } + + /** + * Check if any of the signs need periodic updating. + * @return true if one or more of the signs need periodic updating, otherwise false + */ + public boolean needsPeriodicUpdate() { + boolean result = false; + for(RegionSign sign : signs.values()) { + result = result | sign.needsPeriodicUpdate(); + } + return result; + } + + /** + * Get the signs of this region. + * @return List of signs + */ + public List getSigns() { + return Collections.unmodifiableList(new ArrayList<>(signs.values())); + } + + /** + * Get the signs of this region. + * @return Map with signs: locationString -> RegionSign + */ + Map getSignsRef() { + return signs; + } + + /** + * Get a list with all sign locations. + * @return A List with all sign locations + */ + public List getSignLocations() { + List result = new ArrayList<>(); + for(RegionSign sign : signs.values()) { + result.add(sign.getLocation()); + } + return result; + } + + /** + * Add a sign to this region. + * @param location The location of the sign + * @param signType The type of the sign (WALL_SIGN or SIGN_POST) + * @param facing The orientation of the sign + * @param profile The profile to use with this sign (null for default) + */ + public void addSign(Location location, Material signType, BlockFace facing, String profile) { + int i = 0; + while(getRegion().getConfig().isSet("general.signs." + i)) { + i++; + } + String signPath = "general.signs." + i + "."; + getRegion().setSetting(signPath + "location", Utils.locationToConfig(location)); + getRegion().setSetting(signPath + "facing", facing != null ? facing.name() : null); + getRegion().setSetting(signPath + "signType", signType != null ? signType.name() : null); + if(profile != null && profile.length() != 0) { + getRegion().setSetting(signPath + "profile", profile); + } + // Add to the map + RegionSign sign = new RegionSign(this, i + ""); + signs.put(sign.getStringLocation(), sign); + allSigns.put(sign.getStringLocation(), sign); + signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>()) + .add(sign); + } + + /** + * Checks if there is a sign from this region at the specified location. + * @param location Location to check + * @return true if this region has a sign at the location, otherwise false + */ + public boolean isSignOfRegion(Location location) { + Set signs; + if(getRegion().getConfig().getConfigurationSection("general.signs") == null) { + return false; + } + signs = getRegion().getConfig().getConfigurationSection("general.signs").getKeys(false); + for(String sign : signs) { + Location signLocation = Utils.configToLocation(getRegion().getConfig().getConfigurationSection("general.signs." + sign + ".location")); + if(signLocation != null + && signLocation.getWorld().equals(location.getWorld()) + && signLocation.getBlockX() == location.getBlockX() + && signLocation.getBlockY() == location.getBlockY() + && signLocation.getBlockZ() == location.getBlockZ()) { + return true; + } + } + return false; + } + } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignBreakListener.java b/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignBreakListener.java deleted file mode 100644 index 00cc3ea..0000000 --- a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignBreakListener.java +++ /dev/null @@ -1,121 +0,0 @@ -package me.wiefferink.areashop.listeners; - -import me.wiefferink.areashop.AreaShop; -import me.wiefferink.areashop.features.SignsFeature; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPhysicsEvent; - -/** - * Checks for placement of signs for this plugin. - */ -public final class SignBreakListener implements Listener { - private AreaShop plugin; - - /** - * Constructor. - * @param plugin The AreaShop plugin - */ - public SignBreakListener(AreaShop plugin) { - this.plugin = plugin; - } - - - /** - * Called when a block is broken. - * @param event The event - */ - @EventHandler(priority = EventPriority.HIGH) - public void onSignBreak(BlockBreakEvent event) { - if(event.isCancelled()) { - return; - } - Block block = event.getBlock(); - // Check if it is a sign - if(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST) { - // Check if the rent sign is really the same as a saved rent - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); - if(regionSign == null) { - return; - } - // Remove the sign of the rental region if the player has permission - if(event.getPlayer().hasPermission("areashop.delsign")) { - regionSign.remove(); - plugin.message(event.getPlayer(), "delsign-success", regionSign.getRegion()); - } else { // Cancel the breaking of the sign - event.setCancelled(true); - plugin.message(event.getPlayer(), "delsign-noPermission", regionSign.getRegion()); - } - } - } - - /** - * Called when the physics of a block change. - * @param event The event - */ - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onIndirectSignBreak(BlockPhysicsEvent event) { - if(event.getBlock().getType() == Material.SIGN_POST || event.getBlock().getType() == Material.WALL_SIGN) { - // Check if the rent sign is really the same as a saved rent - if(SignsFeature.getSignByLocation(event.getBlock().getLocation()) != null) { - // Cancel the sign breaking, will create a floating sign but at least it is not disconnected/gone - event.setCancelled(true); - } - } - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignClickListener.java b/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignClickListener.java deleted file mode 100644 index 629756e..0000000 --- a/AreaShop/src/main/java/me/wiefferink/areashop/listeners/SignClickListener.java +++ /dev/null @@ -1,94 +0,0 @@ -package me.wiefferink.areashop.listeners; - -import me.wiefferink.areashop.AreaShop; -import me.wiefferink.areashop.features.SignsFeature; -import me.wiefferink.areashop.regions.GeneralRegion.ClickType; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; - -public class SignClickListener implements Listener { - private AreaShop plugin; - - /** - * Constructor. - * @param plugin The AreaShop plugin - */ - public SignClickListener(AreaShop plugin) { - this.plugin = plugin; - } - - /** - * Called when a player interacts. - * @param event The event - */ - @EventHandler(priority = EventPriority.HIGH) - public void onSignClick(PlayerInteractEvent event) { - if(event.isCancelled()) { - return; - } - Block block = event.getClickedBlock(); - // Check for clicking a sign and rightclicking - if((event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK) - && (block.getType() == Material.SIGN_POST || block.getType() == Material.WALL_SIGN)) { - // Check if the rent sign is really the same as a saved rent - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); - if(regionSign == null) { - return; - } - Player player = event.getPlayer(); - if(plugin.getSignlinkerManager().isInSignLinkMode(player)) { - return; - } - // Get the clicktype - ClickType clickType = null; - if(player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) { - clickType = ClickType.SHIFTLEFTCLICK; - } else if(!player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) { - clickType = ClickType.LEFTCLICK; - } else if(player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - clickType = ClickType.SHIFTRIGHTCLICK; - } else if(!player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - clickType = ClickType.RIGHTCLICK; - } - // Run the commands - boolean ran = regionSign.runSignCommands(player, clickType); - // Only cancel event if at least one command has been executed - event.setCancelled(ran); - } - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/managers/FeatureManager.java b/AreaShop/src/main/java/me/wiefferink/areashop/managers/FeatureManager.java index 434c620..ac2c35b 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/managers/FeatureManager.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/managers/FeatureManager.java @@ -4,9 +4,9 @@ import me.wiefferink.areashop.AreaShop; import me.wiefferink.areashop.features.DebugFeature; import me.wiefferink.areashop.features.FriendsFeature; import me.wiefferink.areashop.features.RegionFeature; -import me.wiefferink.areashop.features.SignsFeature; import me.wiefferink.areashop.features.TeleportFeature; import me.wiefferink.areashop.features.WorldGuardRegionFlagsFeature; +import me.wiefferink.areashop.features.signs.SignsFeature; import me.wiefferink.areashop.regions.GeneralRegion; import java.lang.reflect.Constructor; @@ -45,7 +45,7 @@ public class FeatureManager extends Manager { feature.listen(); globalFeatures.add(feature); } catch(InstantiationException | IllegalAccessException | InvocationTargetException | IllegalArgumentException e) { - AreaShop.error("Failed to instantiate global feature:", clazz); + AreaShop.error("Failed to instantiate global feature:", clazz, e); } } catch(NoSuchMethodException e) { // Feature does not have a global part @@ -80,7 +80,7 @@ public class FeatureManager extends Manager { try { return regionFeatureConstructors.get(featureClazz).newInstance(region); } catch(InstantiationException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) { - AreaShop.error("Failed to instanciate feature", featureClazz, "for region", region); + AreaShop.error("Failed to instanciate feature", featureClazz, "for region", region, e, e.getCause()); } return null; } diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/managers/SignLinkerManager.java b/AreaShop/src/main/java/me/wiefferink/areashop/managers/SignLinkerManager.java index f2d3902..39e05b4 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/managers/SignLinkerManager.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/managers/SignLinkerManager.java @@ -1,6 +1,7 @@ package me.wiefferink.areashop.managers; -import me.wiefferink.areashop.features.SignsFeature; +import me.wiefferink.areashop.features.signs.RegionSign; +import me.wiefferink.areashop.features.signs.SignsFeature; import me.wiefferink.areashop.regions.GeneralRegion; import me.wiefferink.areashop.tools.Utils; import org.bukkit.Bukkit; @@ -124,7 +125,7 @@ public class SignLinkerManager extends Manager implements Listener { return; } - SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); + RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation()); if(regionSign != null) { plugin.message(player, "linksigns-alreadyRegistered", regionSign.getRegion()); return; @@ -151,8 +152,8 @@ public class SignLinkerManager extends Manager implements Listener { private boolean hasSign = false; private boolean hasRegion = false; - public Player linker = null; - public String profile = null; + public Player linker; + public String profile; public GeneralRegion region = null; diff --git a/AreaShop/src/main/java/me/wiefferink/areashop/regions/GeneralRegion.java b/AreaShop/src/main/java/me/wiefferink/areashop/regions/GeneralRegion.java index a74f4dd..cbe1f5a 100644 --- a/AreaShop/src/main/java/me/wiefferink/areashop/regions/GeneralRegion.java +++ b/AreaShop/src/main/java/me/wiefferink/areashop/regions/GeneralRegion.java @@ -10,8 +10,8 @@ import me.wiefferink.areashop.events.NotifyRegionEvent; import me.wiefferink.areashop.events.notify.UpdateRegionEvent; import me.wiefferink.areashop.features.FriendsFeature; import me.wiefferink.areashop.features.RegionFeature; -import me.wiefferink.areashop.features.SignsFeature; import me.wiefferink.areashop.features.TeleportFeature; +import me.wiefferink.areashop.features.signs.SignsFeature; import me.wiefferink.areashop.interfaces.GeneralRegionInterface; import me.wiefferink.areashop.managers.FileManager; import me.wiefferink.areashop.tools.Utils; @@ -257,6 +257,11 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl return region instanceof GeneralRegion && ((GeneralRegion)region).getName().equals(getName()); } + @Override + public int hashCode() { + return getName().hashCode(); + } + /** * Get the config file that is used to store the region information. * @return The config file that stores the region information @@ -440,6 +445,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl } String landlordName = getStringSetting("general.landlordName"); if(landlordName != null && !landlordName.isEmpty()) { + @SuppressWarnings("deprecation") OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(landlordName); if(offlinePlayer != null) { return offlinePlayer.getUniqueId();