diff --git a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java index 8f1e29b..2d717e7 100644 --- a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java +++ b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java @@ -74,6 +74,9 @@ public class Properties { @ConfigurationComment("Do you want to allow using shops to people in creative mode?") public static boolean IGNORE_CREATIVE_MODE = true; + @ConfigurationComment("Do you want to allow using shops to people who have access to it due to their permissions? (owners are always ignored)") + public static boolean IGNORE_ACCESS_PERMS = true; + @ConfigurationComment("If true, people will buy with left-click and sell with right-click.") public static boolean REVERSE_BUTTONS = false; diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/ChestBreak.java b/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/ChestBreak.java index c09aef4..37615e9 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/ChestBreak.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/ChestBreak.java @@ -1,11 +1,11 @@ package com.Acrobot.ChestShop.Listeners.Block.Break; import com.Acrobot.ChestShop.Configuration.Properties; -import com.Acrobot.ChestShop.Listeners.Player.PlayerInteract; -import com.Acrobot.ChestShop.Plugins.ChestShop; +import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Utils.uBlock; import org.bukkit.block.Block; +import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -38,10 +38,14 @@ public class ChestBreak implements Listener { } private static boolean canBeBroken(Block block, Player breaker) { - if (!uBlock.couldBeShopContainer(block) || !Properties.USE_BUILT_IN_PROTECTION || !ChestShopSign.isShopChest(block)) { + if (!uBlock.couldBeShopContainer(block) || !Properties.USE_BUILT_IN_PROTECTION) { return true; } - return breaker != null && (PlayerInteract.canOpenOtherShops(breaker) || ChestShop.canAccess(breaker, block)); + Sign shopSign = uBlock.findAnyNearbyShopSign(block); + if (breaker != null) { + return ChestShopSign.hasPermission(breaker, Permission.OTHER_NAME_DESTROY, shopSign); + } + return shopSign == null; } } diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/SignBreak.java b/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/SignBreak.java index 21fc497..c1ec61c 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/SignBreak.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/Block/Break/SignBreak.java @@ -6,6 +6,7 @@ import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Events.ShopDestroyedEvent; import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.Signs.ChestShopSign; +import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.Utils.uBlock; import org.bukkit.Material; import org.bukkit.block.Block; @@ -30,10 +31,8 @@ import java.util.List; import static com.Acrobot.Breeze.Utils.BlockUtil.getAttachedBlock; import static com.Acrobot.Breeze.Utils.BlockUtil.isSign; -import static com.Acrobot.ChestShop.Permission.ADMIN; -import static com.Acrobot.ChestShop.Permission.MOD; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME_DESTROY; import static com.Acrobot.ChestShop.Signs.ChestShopSign.NAME_LINE; -import static com.Acrobot.ChestShop.UUIDs.NameManager.canUseName; /** * @author Acrobot @@ -151,11 +150,7 @@ public class SignBreak implements Listener { } private static boolean canDestroyShop(Player player, String name) { - return player != null && (hasShopBreakingPermission(player) || canUseName(player, name)); - } - - private static boolean hasShopBreakingPermission(Player player) { - return Permission.has(player, ADMIN) || Permission.has(player, MOD); + return player != null && NameManager.canUseName(player, OTHER_NAME_DESTROY, name); } private static void sendShopDestroyedEvent(Sign sign, Player player) { diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInteract.java b/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInteract.java index aaa2845..26cf614 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInteract.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInteract.java @@ -35,6 +35,9 @@ import static com.Acrobot.Breeze.Utils.BlockUtil.isSign; import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType; import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType.BUY; import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType.SELL; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME_ACCESS; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME_CREATE; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME_DESTROY; import static com.Acrobot.ChestShop.Signs.ChestShopSign.*; import static org.bukkit.event.block.Action.LEFT_CLICK_BLOCK; import static org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK; @@ -58,7 +61,7 @@ public class PlayerInteract implements Listener { return; } - if (!canOpenOtherShops(player) && !ChestShop.canAccess(player, block)) { + if (!ChestShop.canAccess(player, block)) { player.sendMessage(Messages.prefix(Messages.ACCESS_DENIED)); event.setCancelled(true); } @@ -66,7 +69,7 @@ public class PlayerInteract implements Listener { return; } - if (!isSign(block) || player.getItemInHand().getType() == Material.SIGN) // Blocking accidental sign edition + if (!isSign(block) || player.getInventory().getItemInMainHand().getType() == Material.SIGN) // Blocking accidental sign edition return; Sign sign = (Sign) block.getState(); @@ -74,10 +77,8 @@ public class PlayerInteract implements Listener { return; } - boolean canAccess = ChestShopSign.canAccess(player, sign); - if (Properties.ALLOW_AUTO_ITEM_FILL && ChatColor.stripColor(sign.getLine(ITEM_LINE)).equals(AUTOFILL_CODE)) { - if (canAccess) { + if (ChestShopSign.hasPermission(player, OTHER_NAME_CREATE, sign)) { ItemStack item = player.getInventory().getItemInMainHand(); if (!MaterialUtil.isEmpty(item)) { String itemCode = MaterialUtil.getSignName(item); @@ -101,19 +102,22 @@ public class PlayerInteract implements Listener { return; } - if (canAccess && !ChestShopSign.isAdminShop(sign)) { - if (!Properties.ALLOW_SIGN_CHEST_OPEN - || player.isSneaking() || player.isInsideVehicle() - || (Properties.IGNORE_CREATIVE_MODE && player.getGameMode() == GameMode.CREATIVE)) { + if (ChestShopSign.hasPermission(player, OTHER_NAME_ACCESS, sign) && !ChestShopSign.isAdminShop(sign)) { + if (Properties.ALLOW_SIGN_CHEST_OPEN) { + if (player.isSneaking() || player.isInsideVehicle() + || (Properties.IGNORE_CREATIVE_MODE && player.getGameMode() == GameMode.CREATIVE) + || (Properties.ALLOW_LEFT_CLICK_DESTROYING && action == LEFT_CLICK_BLOCK + && ChestShopSign.hasPermission(player, OTHER_NAME_DESTROY, sign))) { + return; + } + + event.setCancelled(true); + showChestGUI(player, block, sign); + return; + } else if (Properties.IGNORE_ACCESS_PERMS || ChestShopSign.isOwner(player, sign)) { + // don't allow owners or people with access to buy/sell at this shop return; } - - if (!Properties.ALLOW_LEFT_CLICK_DESTROYING || action != LEFT_CLICK_BLOCK) { - event.setCancelled(true); - showChestGUI(player, block); - } - - return; } if (action == RIGHT_CLICK_BLOCK) { @@ -212,19 +216,23 @@ public class PlayerInteract implements Listener { } } + /** + * @deprecated Use {@link ChestShopSign#hasPermission(Player, Permission, Sign)} with {@link Permission#OTHER_NAME_ACCESS} + */ + @Deprecated public static boolean canOpenOtherShops(Player player) { - return Permission.has(player, Permission.ADMIN) || Permission.has(player, Permission.MOD); + return Permission.has(player, Permission.OTHER_NAME_ACCESS + ".*"); } - private static void showChestGUI(Player player, Block signBlock) { - Container container = uBlock.findConnectedContainer(signBlock); + private static void showChestGUI(Player player, Block signBlock, Sign sign) { + Container container = uBlock.findConnectedContainer(sign); if (container == null) { player.sendMessage(Messages.prefix(Messages.NO_CHEST_DETECTED)); return; } - if (!canOpenOtherShops(player) && !Security.canAccess(player, signBlock)) { + if (!Security.canAccess(player, signBlock)) { return; } diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInventory.java b/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInventory.java index 87f76a9..67f646c 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInventory.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/Player/PlayerInventory.java @@ -43,7 +43,7 @@ public class PlayerInventory implements Listener { chest = ((DoubleChest) event.getInventory().getHolder()).getLocation().getBlock(); } - if (!PlayerInteract.canOpenOtherShops(player) && !ChestShop.canAccess(player, chest)) { + if (!ChestShop.canAccess(player, chest)) { player.sendMessage(Messages.prefix(Messages.ACCESS_DENIED)); event.setCancelled(true); } diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/NameChecker.java b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/NameChecker.java index 7434e50..3866fcb 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/NameChecker.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/NameChecker.java @@ -9,6 +9,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME_CREATE; import static com.Acrobot.ChestShop.Signs.ChestShopSign.NAME_LINE; import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.UNKNOWN_PLAYER; @@ -22,7 +23,7 @@ public class NameChecker implements Listener { String name = event.getSignLine(NAME_LINE); Player player = event.getPlayer(); - if (name.isEmpty() || !NameManager.canUseName(player, name)) { + if (name.isEmpty() || !NameManager.canUseName(player, OTHER_NAME_CREATE, name)) { Account account = NameManager.getAccount(player.getName()); if (account != null) { event.setSignLine(NAME_LINE, account.getShortName()); diff --git a/src/main/java/com/Acrobot/ChestShop/Permission.java b/src/main/java/com/Acrobot/ChestShop/Permission.java index e468e1d..4a8bee4 100644 --- a/src/main/java/com/Acrobot/ChestShop/Permission.java +++ b/src/main/java/com/Acrobot/ChestShop/Permission.java @@ -4,6 +4,8 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.util.Optional; + /** * @author Acrobot */ @@ -24,8 +26,12 @@ public enum Permission { SELL("ChestShop.shop.sell"), ADMIN("ChestShop.admin"), + ADMIN_SHOP("ChestShop.adminshop"), MOD("ChestShop.mod"), - OTHER_NAME("ChestShop.name."), + OTHER_NAME("ChestShop.name"), + OTHER_NAME_CREATE("ChestShop.othername.create"), + OTHER_NAME_DESTROY("ChestShop.othername.destroy"), + OTHER_NAME_ACCESS("ChestShop.othername.access"), GROUP("ChestShop.group."), NOFEE("ChestShop.nofee"), @@ -48,15 +54,21 @@ public enum Permission { } public static boolean otherName(Player player, String name) { - if (has(player, Permission.ADMIN)) { - return true; - } - - return hasPermissionSet(player, OTHER_NAME + name) || hasPermissionSet(player, OTHER_NAME + name.toLowerCase()); + return otherName(player, OTHER_NAME, name); } - private static boolean hasPermissionSet(CommandSender sender, String permission) { - return sender.isPermissionSet(permission) && sender.hasPermission(permission); + public static boolean otherName(Player player, Permission base, String name) { + boolean hasBase = base != OTHER_NAME && otherName(player, OTHER_NAME, name); + if (hasBase || has(player, base + ".*")) { + return !hasPermissionSetFalse(player, base+ "." + name) && !hasPermissionSetFalse(player, base + "." + name.toLowerCase()); + } + + return has(player, base + "." + name) || has(player, base + "." + name.toLowerCase()); + } + + private static boolean hasPermissionSetFalse(CommandSender sender, String permission) { + return (sender.isPermissionSet(permission) && !sender.hasPermission(permission)) + || (sender.isPermissionSet(permission.toLowerCase()) && !sender.hasPermission(permission.toLowerCase())); } public static org.bukkit.permissions.Permission getPermission(Permission permission) { diff --git a/src/main/java/com/Acrobot/ChestShop/Plugins/ChestShop.java b/src/main/java/com/Acrobot/ChestShop/Plugins/ChestShop.java index f3d027b..f84f383 100644 --- a/src/main/java/com/Acrobot/ChestShop/Plugins/ChestShop.java +++ b/src/main/java/com/Acrobot/ChestShop/Plugins/ChestShop.java @@ -33,7 +33,7 @@ public class ChestShop implements Listener { } public static boolean canAccess(Player player, Block block) { - if (Permission.has(player, Permission.ADMIN) || !canBeProtected(block)) { + if (!canBeProtected(block)) { return true; } @@ -65,6 +65,6 @@ public class ChestShop implements Listener { } private static boolean isShopMember(Player player, Sign sign) { - return NameManager.canUseName(player, sign.getLine(ChestShopSign.NAME_LINE)); + return ChestShopSign.hasPermission(player, Permission.OTHER_NAME_ACCESS, sign); } } diff --git a/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java b/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java index 32fc648..2a578c2 100644 --- a/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java +++ b/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java @@ -4,6 +4,8 @@ import com.Acrobot.Breeze.Utils.BlockUtil; import com.Acrobot.Breeze.Utils.StringUtil; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Containers.AdminInventory; +import com.Acrobot.ChestShop.Database.Account; +import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.Utils.uBlock; import org.bukkit.block.Block; @@ -102,13 +104,30 @@ public class ChestShopSign { } public static boolean canAccess(Player player, Sign sign) { + return hasPermission(player, Permission.OTHER_NAME_ACCESS, sign); + } + + public static boolean hasPermission(Player player, Permission base, Sign sign) { if (player == null) return false; if (sign == null) return true; String name = sign.getLine(NAME_LINE); if (name == null || name.isEmpty()) return true; - return NameManager.canUseName(player, name); + return NameManager.canUseName(player, base, name); + } + + public static boolean isOwner(Player player, Sign sign) { + if (player == null || sign == null) return false; + + String name = sign.getLine(NAME_LINE); + if (name == null || name.isEmpty()) return false; + + Account account = NameManager.getAccountFromShortName(name); + if (account == null) { + return player.getName().equalsIgnoreCase(name); + } + return account.getUuid().equals(player.getUniqueId()); } public static boolean isValidPreparedSign(String[] lines) { diff --git a/src/main/java/com/Acrobot/ChestShop/Signs/RestrictedSign.java b/src/main/java/com/Acrobot/ChestShop/Signs/RestrictedSign.java index 5979987..ebe59dc 100644 --- a/src/main/java/com/Acrobot/ChestShop/Signs/RestrictedSign.java +++ b/src/main/java/com/Acrobot/ChestShop/Signs/RestrictedSign.java @@ -58,8 +58,9 @@ public class RestrictedSign implements Listener { Sign sign = (Sign) connectedSign.getState(); - if (!ChestShopSign.canAccess(player, sign)) { + if (!ChestShopSign.hasPermission(player, Permission.OTHER_NAME_DESTROY, sign)) { dropSignAndCancelEvent(event); + return; } player.sendMessage(Messages.prefix(Messages.RESTRICTED_SIGN_CREATED)); @@ -129,16 +130,11 @@ public class RestrictedSign implements Listener { public static boolean canAccess(Sign sign, Player player) { Block blockUp = sign.getBlock().getRelative(BlockFace.UP); return !BlockUtil.isSign(blockUp) || hasPermission(player, ((Sign) blockUp.getState()).getLines()); - } public static boolean canDestroy(Player player, Sign sign) { - if (Permission.has(player, ADMIN)) { - return true; - } - Sign shopSign = getAssociatedSign(sign); - return ChestShopSign.canAccess(player, shopSign); + return ChestShopSign.hasPermission(player, Permission.OTHER_NAME_DESTROY, shopSign); } public static Sign getAssociatedSign(Sign restricted) { @@ -152,7 +148,7 @@ public class RestrictedSign implements Listener { } for (String line : lines) { - if (p.hasPermission(Permission.GROUP.toString() + line)) { + if (Permission.has(p, Permission.GROUP + line)) { return true; } } diff --git a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java index 5358d1a..00e14bb 100644 --- a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java +++ b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java @@ -22,6 +22,8 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import static com.Acrobot.ChestShop.Permission.OTHER_NAME; + /** * Lets you save/cache username and UUID relations * @@ -295,18 +297,26 @@ public class NameManager { return shortenedName; } + /** + * @deprecated Use {@link #canUseName(Player, Permission, String)} to provide specific information about how the player wants to use the name + */ + @Deprecated public static boolean canUseName(Player player, String name) { + return canUseName(player, OTHER_NAME, name); + } + + public static boolean canUseName(Player player, Permission base, String name) { if (ChestShopSign.isAdminShop(name)) { - return Permission.has(player, Permission.ADMIN); + return Permission.has(player, Permission.ADMIN_SHOP); } - if (Permission.otherName(player, name)) { + if (Permission.otherName(player, base, name)) { return true; } Account account = getAccountFromShortName(name, false); return account != null && (account.getUuid().equals(player.getUniqueId()) - || (!account.getName().equalsIgnoreCase(name) && Permission.otherName(player, account.getName()))); + || (!account.getName().equalsIgnoreCase(name) && Permission.otherName(player, base, account.getName()))); } public static boolean isAdminShop(UUID uuid) { diff --git a/src/main/java/com/Acrobot/ChestShop/Utils/uBlock.java b/src/main/java/com/Acrobot/ChestShop/Utils/uBlock.java index d2e1db1..980792d 100644 --- a/src/main/java/com/Acrobot/ChestShop/Utils/uBlock.java +++ b/src/main/java/com/Acrobot/ChestShop/Utils/uBlock.java @@ -3,6 +3,7 @@ package com.Acrobot.ChestShop.Utils; import com.Acrobot.Breeze.Utils.BlockUtil; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Signs.ChestShopSign; +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; @@ -67,7 +68,7 @@ public class uBlock { } /** - * @deprecated Use {@link #findConnectedContainer(Block, BlockFace)} + * @deprecated Use {@link #findConnectedContainer(Location, BlockFace)} */ @Deprecated private static org.bukkit.block.Chest findConnectedChest(Block block, BlockFace signFace) { @@ -94,7 +95,7 @@ public class uBlock { if (((org.bukkit.material.Sign) sign.getData()).isWallSign()) { signFace = ((Attachable) sign.getData()).getAttachedFace(); } - return findConnectedContainer(sign.getBlock(), signFace); + return findConnectedContainer(sign.getLocation(), signFace); } public static Container findConnectedContainer(Block block) { @@ -105,12 +106,12 @@ public class uBlock { signFace = ((Attachable) sign.getData()).getAttachedFace(); } } - return findConnectedContainer(block, signFace); + return findConnectedContainer(block.getLocation(), signFace); } - private static Container findConnectedContainer(Block block, BlockFace signFace) { + private static Container findConnectedContainer(Location location, BlockFace signFace) { if (signFace != null) { - Block faceBlock = block.getRelative(signFace); + Block faceBlock = location.clone().add(signFace.getModX(), signFace.getModY(), signFace.getModZ()).getBlock(); if (uBlock.couldBeShopContainer(faceBlock)) { return (Container) faceBlock.getState(); } @@ -118,7 +119,7 @@ public class uBlock { for (BlockFace bf : SHOP_FACES) { if (bf != signFace) { - Block faceBlock = block.getRelative(bf); + Block faceBlock = location.clone().add(bf.getModX(), bf.getModY(), bf.getModZ()).getBlock(); if (uBlock.couldBeShopContainer(faceBlock)) { return (Container) faceBlock.getState(); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2b4d6bc..a2c1447 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -71,10 +71,35 @@ permissions: ChestShop.admin: description: Allows user to modify/destroy other stores and create an Admin Shops default: op + children: + ChestShop.adminshop: true + ChestShop.name.*: true + ChestShop.adminshop: + description: Allows user to create and destroy Admin Shops ChestShop.mod: description: Allows user only to view other store chests, he can't destroy them or create an Admin Shop + children: + ChestShop.name.access.*: true + ChestShop.name.*: + description: Gives you the power to do create and access shops for all names. + children: + ChestShop.name.create.*: true + ChestShop.name.destroy.*: true + ChestShop.name.access.*: true ChestShop.name.(some name): + description: Gives you the power to do create and access shops for (some name), for example your town. + ChestShop.name.create.*: + description: Gives you the power to do create shops for all names. + ChestShop.name.create.(some name): description: Gives you the power to do create shops for (some name), for example your town. + ChestShop.name.destroy.*: + description: Gives you the power to do destroy shops with all names. + ChestShop.name.destroy.(some name): + description: Gives you the power to do destroy shops with (some name), for example your town. + ChestShop.name.access.*: + description: Gives you the power to do access shops for all names. + ChestShop.name.access.(some name): + description: Gives you the power to do access shops for (some name), for example your town. ChestShop.shop.create.food: description: Allows to create a shop that sells food children: